(function (a, b) {
    function cy(a) {
        return f.isWindow(a) ? a : a.nodeType === 9 ? a.defaultView || a.parentWindow : !1
    }
    function cv(a) {
        if (!cj[a]) {
            var b = f("<" + a + ">").appendTo("body"),
                d = b.css("display");
            b.remove();
            if (d === "none" || d === "") {
                ck || (ck = c.createElement("iframe"), ck.frameBorder = ck.width = ck.height = 0), c.body.appendChild(ck);
                if (!cl || !ck.createElement) cl = (ck.contentWindow || ck.contentDocument).document, cl.write("<!doctype><html><body></body></html>");
                b = cl.createElement(a), cl.body.appendChild(b), d = f.css(b, "display"), c.body.removeChild(ck)
            }
            cj[a] = d
        }
        return cj[a]
    }
    function cu(a, b) {
        var c = {};
        f.each(cp.concat.apply([], cp.slice(0, b)), function () {
            c[this] = a
        });
        return c
    }
    function ct() {
        cq = b
    }
    function cs() {
        setTimeout(ct, 0);
        return cq = f.now()
    }
    function ci() {
        try {
            return new a.ActiveXObject("Microsoft.XMLHTTP")
        } catch (b) {}
    }
    function ch() {
        try {
            return new a.XMLHttpRequest
        } catch (b) {}
    }
    function cb(a, c) {
        a.dataFilter && (c = a.dataFilter(c, a.dataType));
        var d = a.dataTypes,
            e = {},
            g, h, i = d.length,
            j, k = d[0],
            l, m, n, o, p;
        for (g = 1; g < i; g++) {
            if (g === 1) for (h in a.converters) typeof h == "string" && (e[h.toLowerCase()] = a.converters[h]);
            l = k, k = d[g];
            if (k === "*") k = l;
            else if (l !== "*" && l !== k) {
                m = l + " " + k, n = e[m] || e["* " + k];
                if (!n) {
                    p = b;
                    for (o in e) {
                        j = o.split(" ");
                        if (j[0] === l || j[0] === "*") {
                            p = e[j[1] + " " + k];
                            if (p) {
                                o = e[o], o === !0 ? n = p : p === !0 && (n = o);
                                break
                            }
                        }
                    }
                }!n && !p && f.error("No conversion from " + m.replace(" ", " to ")), n !== !0 && (c = n ? n(c) : p(o(c)))
            }
        }
        return c
    }
    function ca(a, c, d) {
        var e = a.contents,
            f = a.dataTypes,
            g = a.responseFields,
            h, i, j, k;
        for (i in g) i in d && (c[g[i]] = d[i]);
        while (f[0] === "*") f.shift(), h === b && (h = a.mimeType || c.getResponseHeader("content-type"));
        if (h) for (i in e) if (e[i] && e[i].test(h)) {
            f.unshift(i);
            break
        }
        if (f[0] in d) j = f[0];
        else {
            for (i in d) {
                if (!f[0] || a.converters[i + " " + f[0]]) {
                    j = i;
                    break
                }
                k || (k = i)
            }
            j = j || k
        }
        if (j) {
            j !== f[0] && f.unshift(j);
            return d[j]
        }
    }
    function b_(a, b, c, d) {
        if (f.isArray(b)) f.each(b, function (b, e) {
            c || bF.test(a) ? d(a, e) : b_(a + "[" + (typeof e == "object" || f.isArray(e) ? b : "") + "]", e, c, d)
        });
        else if (!c && b != null && typeof b == "object") for (var e in b) b_(a + "[" + e + "]", b[e], c, d);
        else d(a, b)
    }
    function b$(a, c, d, e, f, g) {
        f = f || c.dataTypes[0], g = g || {}, g[f] = !0;
        var h = a[f],
            i = 0,
            j = h ? h.length : 0,
            k = a === bU,
            l;
        for (; i < j && (k || !l); i++) l = h[i](c, d, e), typeof l == "string" && (!k || g[l] ? l = b : (c.dataTypes.unshift(l), l = b$(a, c, d, e, l, g)));
        (k || !l) && !g["*"] && (l = b$(a, c, d, e, "*", g));
        return l
    }
    function bZ(a) {
        return function (b, c) {
            typeof b != "string" && (c = b, b = "*");
            if (f.isFunction(c)) {
                var d = b.toLowerCase().split(bQ),
                    e = 0,
                    g = d.length,
                    h, i, j;
                for (; e < g; e++) h = d[e], j = /^\+/.test(h), j && (h = h.substr(1) || "*"), i = a[h] = a[h] || [], i[j ? "unshift" : "push"](c)
            }
        }
    }
    function bD(a, b, c) {
        var d = b === "width" ? bx : by,
            e = b === "width" ? a.offsetWidth : a.offsetHeight;
        if (c === "border") return e;
        f.each(d, function () {
            c || (e -= parseFloat(f.css(a, "padding" + this)) || 0), c === "margin" ? e += parseFloat(f.css(a, "margin" + this)) || 0 : e -= parseFloat(f.css(a, "border" + this + "Width")) || 0
        });
        return e
    }
    function bn(a, b) {
        b.src ? f.ajax({
            url: b.src,
            async: !1,
            dataType: "script"
        }) : f.globalEval((b.text || b.textContent || b.innerHTML || "").replace(bf, "/*$0*/")), b.parentNode && b.parentNode.removeChild(b)
    }
    function bm(a) {
        f.nodeName(a, "input") ? bl(a) : a.getElementsByTagName && f.grep(a.getElementsByTagName("input"), bl)
    }
    function bl(a) {
        if (a.type === "checkbox" || a.type === "radio") a.defaultChecked = a.checked
    }
    function bk(a) {
        return "getElementsByTagName" in a ? a.getElementsByTagName("*") : "querySelectorAll" in a ? a.querySelectorAll("*") : []
    }
    function bj(a, b) {
        var c;
        if (b.nodeType === 1) {
            b.clearAttributes && b.clearAttributes(), b.mergeAttributes && b.mergeAttributes(a), c = b.nodeName.toLowerCase();
            if (c === "object") b.outerHTML = a.outerHTML;
            else if (c !== "input" || a.type !== "checkbox" && a.type !== "radio") {
                if (c === "option") b.selected = a.defaultSelected;
                else if (c === "input" || c === "textarea") b.defaultValue = a.defaultValue
            } else a.checked && (b.defaultChecked = b.checked = a.checked), b.value !== a.value && (b.value = a.value);
            b.removeAttribute(f.expando)
        }
    }
    function bi(a, b) {
        if (b.nodeType === 1 && !! f.hasData(a)) {
            var c = f.expando,
                d = f.data(a),
                e = f.data(b, d);
            if (d = d[c]) {
                var g = d.events;
                e = e[c] = f.extend({}, d);
                if (g) {
                    delete e.handle, e.events = {};
                    for (var h in g) for (var i = 0, j = g[h].length; i < j; i++) f.event.add(b, h + (g[h][i].namespace ? "." : "") + g[h][i].namespace, g[h][i], g[h][i].data)
                }
            }
        }
    }
    function bh(a, b) {
        return f.nodeName(a, "table") ? a.getElementsByTagName("tbody")[0] || a.appendChild(a.ownerDocument.createElement("tbody")) : a
    }
    function X(a, b, c) {
        b = b || 0;
        if (f.isFunction(b)) return f.grep(a, function (a, d) {
            var e = !! b.call(a, d, a);
            return e === c
        });
        if (b.nodeType) return f.grep(a, function (a, d) {
            return a === b === c
        });
        if (typeof b == "string") {
            var d = f.grep(a, function (a) {
                return a.nodeType === 1
            });
            if (S.test(b)) return f.filter(b, d, !c);
            b = f.filter(b, d)
        }
        return f.grep(a, function (a, d) {
            return f.inArray(a, b) >= 0 === c
        })
    }
    function W(a) {
        return !a || !a.parentNode || a.parentNode.nodeType === 11
    }
    function O(a, b) {
        return (a && a !== "*" ? a + "." : "") + b.replace(A, "`").replace(B, "&")
    }
    function N(a) {
        var b, c, d, e, g, h, i, j, k, l, m, n, o, p = [],
            q = [],
            r = f._data(this, "events");
        if (!(a.liveFired === this || !r || !r.live || a.target.disabled || a.button && a.type === "click")) {
            a.namespace && (n = new RegExp("(^|\\.)" + a.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)")), a.liveFired = this;
            var s = r.live.slice(0);
            for (i = 0; i < s.length; i++) g = s[i], g.origType.replace(y, "") === a.type ? q.push(g.selector) : s.splice(i--, 1);
            e = f(a.target).closest(q, a.currentTarget);
            for (j = 0, k = e.length; j < k; j++) {
                m = e[j];
                for (i = 0; i < s.length; i++) {
                    g = s[i];
                    if (m.selector === g.selector && (!n || n.test(g.namespace)) && !m.elem.disabled) {
                        h = m.elem, d = null;
                        if (g.preType === "mouseenter" || g.preType === "mouseleave") a.type = g.preType, d = f(a.relatedTarget).closest(g.selector)[0], d && f.contains(h, d) && (d = h);
                        (!d || d !== h) && p.push({
                            elem: h,
                            handleObj: g,
                            level: m.level
                        })
                    }
                }
            }
            for (j = 0, k = p.length; j < k; j++) {
                e = p[j];
                if (c && e.level > c) break;
                a.currentTarget = e.elem, a.data = e.handleObj.data, a.handleObj = e.handleObj, o = e.handleObj.origHandler.apply(e.elem, arguments);
                if (o === !1 || a.isPropagationStopped()) {
                    c = e.level, o === !1 && (b = !1);
                    if (a.isImmediatePropagationStopped()) break
                }
            }
            return b
        }
    }
    function L(a, c, d) {
        var e = f.extend({}, d[0]);
        e.type = a, e.originalEvent = {}, e.liveFired = b, f.event.handle.call(c, e), e.isDefaultPrevented() && d[0].preventDefault()
    }
    function F() {
        return !0
    }
    function E() {
        return !1
    }
    function m(a, c, d) {
        var e = c + "defer",
            g = c + "queue",
            h = c + "mark",
            i = f.data(a, e, b, !0);
        i && (d === "queue" || !f.data(a, g, b, !0)) && (d === "mark" || !f.data(a, h, b, !0)) && setTimeout(function () {
            !f.data(a, g, b, !0) && !f.data(a, h, b, !0) && (f.removeData(a, e, !0), i.resolve())
        }, 0)
    }
    function l(a) {
        for (var b in a) if (b !== "toJSON") return !1;
        return !0
    }
    function k(a, c, d) {
        if (d === b && a.nodeType === 1) {
            var e = "data-" + c.replace(j, "$1-$2").toLowerCase();
            d = a.getAttribute(e);
            if (typeof d == "string") {
                try {
                    d = d === "true" ? !0 : d === "false" ? !1 : d === "null" ? null : f.isNaN(d) ? i.test(d) ? f.parseJSON(d) : d : parseFloat(d)
                } catch (g) {}
                f.data(a, c, d)
            } else d = b
        }
        return d
    }
    var c = a.document,
        d = a.navigator,
        e = a.location,
        f = function () {
            function H() {
                if (!e.isReady) {
                    try {
                        c.documentElement.doScroll("left")
                    } catch (a) {
                        setTimeout(H, 1);
                        return
                    }
                    e.ready()
                }
            }
            var e = function (a, b) {
                    return new e.fn.init(a, b, h)
                },
                f = a.jQuery,
                g = a.$,
                h, i = /^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
                j = /\S/,
                k = /^\s+/,
                l = /\s+$/,
                m = /\d/,
                n = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
                o = /^[\],:{}\s]*$/,
                p = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
                q = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
                r = /(?:^|:|,)(?:\s*\[)+/g,
                s = /(webkit)[ \/]([\w.]+)/,
                t = /(opera)(?:.*version)?[ \/]([\w.]+)/,
                u = /(msie) ([\w.]+)/,
                v = /(mozilla)(?:.*? rv:([\w.]+))?/,
                w = d.userAgent,
                x, y, z, A = Object.prototype.toString,
                B = Object.prototype.hasOwnProperty,
                C = Array.prototype.push,
                D = Array.prototype.slice,
                E = String.prototype.trim,
                F = Array.prototype.indexOf,
                G = {};
            e.fn = e.prototype = {
                constructor: e,
                init: function (a, d, f) {
                    var g, h, j, k;
                    if (!a) return this;
                    if (a.nodeType) {
                        this.context = this[0] = a, this.length = 1;
                        return this
                    }
                    if (a === "body" && !d && c.body) {
                        this.context = c, this[0] = c.body, this.selector = a, this.length = 1;
                        return this
                    }
                    if (typeof a == "string") {
                        a.charAt(0) !== "<" || a.charAt(a.length - 1) !== ">" || a.length < 3 ? g = i.exec(a) : g = [null, a, null];
                        if (g && (g[1] || !d)) {
                            if (g[1]) {
                                d = d instanceof e ? d[0] : d, k = d ? d.ownerDocument || d : c, j = n.exec(a), j ? e.isPlainObject(d) ? (a = [c.createElement(j[1])], e.fn.attr.call(a, d, !0)) : a = [k.createElement(j[1])] : (j = e.buildFragment([g[1]], [k]), a = (j.cacheable ? e.clone(j.fragment) : j.fragment).childNodes);
                                return e.merge(this, a)
                            }
                            h = c.getElementById(g[2]);
                            if (h && h.parentNode) {
                                if (h.id !== g[2]) return f.find(a);
                                this.length = 1, this[0] = h
                            }
                            this.context = c, this.selector = a;
                            return this
                        }
                        return !d || d.jquery ? (d || f).find(a) : this.constructor(d).find(a)
                    }
                    if (e.isFunction(a)) return f.ready(a);
                    a.selector !== b && (this.selector = a.selector, this.context = a.context);
                    return e.makeArray(a, this)
                },
                selector: "",
                jquery: "1.6.1",
                length: 0,
                size: function () {
                    return this.length
                },
                toArray: function () {
                    return D.call(this, 0)
                },
                get: function (a) {
                    return a == null ? this.toArray() : a < 0 ? this[this.length + a] : this[a]
                },
                pushStack: function (a, b, c) {
                    var d = this.constructor();
                    e.isArray(a) ? C.apply(d, a) : e.merge(d, a), d.prevObject = this, d.context = this.context, b === "find" ? d.selector = this.selector + (this.selector ? " " : "") + c : b && (d.selector = this.selector + "." + b + "(" + c + ")");
                    return d
                },
                each: function (a, b) {
                    return e.each(this, a, b)
                },
                ready: function (a) {
                    e.bindReady(), y.done(a);
                    return this
                },
                eq: function (a) {
                    return a === -1 ? this.slice(a) : this.slice(a, +a + 1)
                },
                first: function () {
                    return this.eq(0)
                },
                last: function () {
                    return this.eq(-1)
                },
                slice: function () {
                    return this.pushStack(D.apply(this, arguments), "slice", D.call(arguments).join(","))
                },
                map: function (a) {
                    return this.pushStack(e.map(this, function (b, c) {
                        return a.call(b, c, b)
                    }))
                },
                end: function () {
                    return this.prevObject || this.constructor(null)
                },
                push: C,
                sort: [].sort,
                splice: [].splice
            }, e.fn.init.prototype = e.fn, e.extend = e.fn.extend = function () {
                var a, c, d, f, g, h, i = arguments[0] || {},
                    j = 1,
                    k = arguments.length,
                    l = !1;
                typeof i == "boolean" && (l = i, i = arguments[1] || {}, j = 2), typeof i != "object" && !e.isFunction(i) && (i = {}), k === j && (i = this, --j);
                for (; j < k; j++) if ((a = arguments[j]) != null) for (c in a) {
                    d = i[c], f = a[c];
                    if (i === f) continue;
                    l && f && (e.isPlainObject(f) || (g = e.isArray(f))) ? (g ? (g = !1, h = d && e.isArray(d) ? d : []) : h = d && e.isPlainObject(d) ? d : {}, i[c] = e.extend(l, h, f)) : f !== b && (i[c] = f)
                }
                return i
            }, e.extend({
                noConflict: function (b) {
                    a.$ === e && (a.$ = g), b && a.jQuery === e && (a.jQuery = f);
                    return e
                },
                isReady: !1,
                readyWait: 1,
                holdReady: function (a) {
                    a ? e.readyWait++ : e.ready(!0)
                },
                ready: function (a) {
                    if (a === !0 && !--e.readyWait || a !== !0 && !e.isReady) {
                        if (!c.body) return setTimeout(e.ready, 1);
                        e.isReady = !0;
                        if (a !== !0 && --e.readyWait > 0) return;
                        y.resolveWith(c, [e]), e.fn.trigger && e(c).trigger("ready").unbind("ready")
                    }
                },
                bindReady: function () {
                    if (!y) {
                        y = e._Deferred();
                        if (c.readyState === "complete") return setTimeout(e.ready, 1);
                        if (c.addEventListener) c.addEventListener("DOMContentLoaded", z, !1), a.addEventListener("load", e.ready, !1);
                        else if (c.attachEvent) {
                            c.attachEvent("onreadystatechange", z), a.attachEvent("onload", e.ready);
                            var b = !1;
                            try {
                                b = a.frameElement == null
                            } catch (d) {}
                            c.documentElement.doScroll && b && H()
                        }
                    }
                },
                isFunction: function (a) {
                    return e.type(a) === "function"
                },
                isArray: Array.isArray ||
                function (a) {
                    return e.type(a) === "array"
                },
                isWindow: function (a) {
                    return a && typeof a == "object" && "setInterval" in a
                },
                isNaN: function (a) {
                    return a == null || !m.test(a) || isNaN(a)
                },
                type: function (a) {
                    return a == null ? String(a) : G[A.call(a)] || "object"
                },
                isPlainObject: function (a) {
                    if (!a || e.type(a) !== "object" || a.nodeType || e.isWindow(a)) return !1;
                    if (a.constructor && !B.call(a, "constructor") && !B.call(a.constructor.prototype, "isPrototypeOf")) return !1;
                    var c;
                    for (c in a);
                    return c === b || B.call(a, c)
                },
                isEmptyObject: function (a) {
                    for (var b in a) return !1;
                    return !0
                },
                error: function (a) {
                    throw a
                },
                parseJSON: function (b) {
                    if (typeof b != "string" || !b) return null;
                    b = e.trim(b);
                    if (a.JSON && a.JSON.parse) return a.JSON.parse(b);
                    if (o.test(b.replace(p, "@").replace(q, "]").replace(r, ""))) return (new Function("return " + b))();
                    e.error("Invalid JSON: " + b)
                },
                parseXML: function (b, c, d) {
                    a.DOMParser ? (d = new DOMParser, c = d.parseFromString(b, "text/xml")) : (c = new ActiveXObject("Microsoft.XMLDOM"), c.async = "false", c.loadXML(b)), d = c.documentElement, (!d || !d.nodeName || d.nodeName === "parsererror") && e.error("Invalid XML: " + b);
                    return c
                },
                noop: function () {},
                globalEval: function (b) {
                    b && j.test(b) && (a.execScript ||
                    function (b) {
                        a.eval.call(a, b)
                    })(b)
                },
                nodeName: function (a, b) {
                    return a.nodeName && a.nodeName.toUpperCase() === b.toUpperCase()
                },
                each: function (a, c, d) {
                    var f, g = 0,
                        h = a.length,
                        i = h === b || e.isFunction(a);
                    if (d) {
                        if (i) {
                            for (f in a) if (c.apply(a[f], d) === !1) break
                        } else for (; g < h;) if (c.apply(a[g++], d) === !1) break
                    } else if (i) {
                        for (f in a) if (c.call(a[f], f, a[f]) === !1) break
                    } else for (; g < h;) if (c.call(a[g], g, a[g++]) === !1) break;
                    return a
                },
                trim: E ?
                function (a) {
                    return a == null ? "" : E.call(a)
                } : function (a) {
                    return a == null ? "" : (a + "").replace(k, "").replace(l, "")
                },
                makeArray: function (a, b) {
                    var c = b || [];
                    if (a != null) {
                        var d = e.type(a);
                        a.length == null || d === "string" || d === "function" || d === "regexp" || e.isWindow(a) ? C.call(c, a) : e.merge(c, a)
                    }
                    return c
                },
                inArray: function (a, b) {
                    if (F) return F.call(b, a);
                    for (var c = 0, d = b.length; c < d; c++) if (b[c] === a) return c;
                    return -1
                },
                merge: function (a, c) {
                    var d = a.length,
                        e = 0;
                    if (typeof c.length == "number") for (var f = c.length; e < f; e++) a[d++] = c[e];
                    else while (c[e] !== b) a[d++] = c[e++];
                    a.length = d;
                    return a
                },
                grep: function (a, b, c) {
                    var d = [],
                        e;
                    c = !! c;
                    for (var f = 0, g = a.length; f < g; f++) e = !! b(a[f], f), c !== e && d.push(a[f]);
                    return d
                },
                map: function (a, c, d) {
                    var f, g, h = [],
                        i = 0,
                        j = a.length,
                        k = a instanceof e || j !== b && typeof j == "number" && (j > 0 && a[0] && a[j - 1] || j === 0 || e.isArray(a));
                    if (k) for (; i < j; i++) f = c(a[i], i, d), f != null && (h[h.length] = f);
                    else for (g in a) f = c(a[g], g, d), f != null && (h[h.length] = f);
                    return h.concat.apply([], h)
                },
                guid: 1,
                proxy: function (a, c) {
                    if (typeof c == "string") {
                        var d = a[c];
                        c = a, a = d
                    }
                    if (!e.isFunction(a)) return b;
                    var f = D.call(arguments, 2),
                        g = function () {
                            return a.apply(c, f.concat(D.call(arguments)))
                        };
                    g.guid = a.guid = a.guid || g.guid || e.guid++;
                    return g
                },
                access: function (a, c, d, f, g, h) {
                    var i = a.length;
                    if (typeof c == "object") {
                        for (var j in c) e.access(a, j, c[j], f, g, d);
                        return a
                    }
                    if (d !== b) {
                        f = !h && f && e.isFunction(d);
                        for (var k = 0; k < i; k++) g(a[k], c, f ? d.call(a[k], k, g(a[k], c)) : d, h);
                        return a
                    }
                    return i ? g(a[0], c) : b
                },
                now: function () {
                    return (new Date).getTime()
                },
                uaMatch: function (a) {
                    a = a.toLowerCase();
                    var b = s.exec(a) || t.exec(a) || u.exec(a) || a.indexOf("compatible") < 0 && v.exec(a) || [];
                    return {
                        browser: b[1] || "",
                        version: b[2] || "0"
                    }
                },
                sub: function () {
                    function a(b, c) {
                        return new a.fn.init(b, c)
                    }
                    e.extend(!0, a, this), a.superclass = this, a.fn = a.prototype = this(), a.fn.constructor = a, a.sub = this.sub, a.fn.init = function (d, f) {
                        f && f instanceof e && !(f instanceof a) && (f = a(f));
                        return e.fn.init.call(this, d, f, b)
                    }, a.fn.init.prototype = a.fn;
                    var b = a(c);
                    return a
                },
                browser: {}
            }), e.each("Boolean Number String Function Array Date RegExp Object".split(" "), function (a, b) {
                G["[object " + b + "]"] = b.toLowerCase()
            }), x = e.uaMatch(w), x.browser && (e.browser[x.browser] = !0, e.browser.version = x.version), e.browser.webkit && (e.browser.safari = !0), j.test(" ") && (k = /^[\s\xA0]+/, l = /[\s\xA0]+$/), h = e(c), c.addEventListener ? z = function () {
                c.removeEventListener("DOMContentLoaded", z, !1), e.ready()
            } : c.attachEvent && (z = function () {
                c.readyState === "complete" && (c.detachEvent("onreadystatechange", z), e.ready())
            });
            return e
        }(),
        g = "done fail isResolved isRejected promise then always pipe".split(" "),
        h = [].slice;
    f.extend({
        _Deferred: function () {
            var a = [],
                b, c, d, e = {
                    done: function () {
                        if (!d) {
                            var c = arguments,
                                g, h, i, j, k;
                            b && (k = b, b = 0);
                            for (g = 0, h = c.length; g < h; g++) i = c[g], j = f.type(i), j === "array" ? e.done.apply(e, i) : j === "function" && a.push(i);
                            k && e.resolveWith(k[0], k[1])
                        }
                        return this
                    },
                    resolveWith: function (e, f) {
                        if (!d && !b && !c) {
                            f = f || [], c = 1;
                            try {
                                while (a[0]) a.shift().apply(e, f)
                            } finally {
                                b = [e, f], c = 0
                            }
                        }
                        return this
                    },
                    resolve: function () {
                        e.resolveWith(this, arguments);
                        return this
                    },
                    isResolved: function () {
                        return !!c || !! b
                    },
                    cancel: function () {
                        d = 1, a = [];
                        return this
                    }
                };
            return e
        },
        Deferred: function (a) {
            var b = f._Deferred(),
                c = f._Deferred(),
                d;
            f.extend(b, {
                then: function (a, c) {
                    b.done(a).fail(c);
                    return this
                },
                always: function () {
                    return b.done.apply(b, arguments).fail.apply(this, arguments)
                },
                fail: c.done,
                rejectWith: c.resolveWith,
                reject: c.resolve,
                isRejected: c.isResolved,
                pipe: function (a, c) {
                    return f.Deferred(function (d) {
                        f.each({
                            done: [a, "resolve"],
                            fail: [c, "reject"]
                        }, function (a, c) {
                            var e = c[0],
                                g = c[1],
                                h;
                            f.isFunction(e) ? b[a](function () {
                                h = e.apply(this, arguments), h && f.isFunction(h.promise) ? h.promise().then(d.resolve, d.reject) : d[g](h)
                            }) : b[a](d[g])
                        })
                    }).promise()
                },
                promise: function (a) {
                    if (a == null) {
                        if (d) return d;
                        d = a = {}
                    }
                    var c = g.length;
                    while (c--) a[g[c]] = b[g[c]];
                    return a
                }
            }), b.done(c.cancel).fail(b.cancel), delete b.cancel, a && a.call(b, b);
            return b
        },
        when: function (a) {
            function i(a) {
                return function (c) {
                    b[a] = arguments.length > 1 ? h.call(arguments, 0) : c, --e || g.resolveWith(g, h.call(b, 0))
                }
            }
            var b = arguments,
                c = 0,
                d = b.length,
                e = d,
                g = d <= 1 && a && f.isFunction(a.promise) ? a : f.Deferred();
            if (d > 1) {
                for (; c < d; c++) b[c] && f.isFunction(b[c].promise) ? b[c].promise().then(i(c), g.reject) : --e;
                e || g.resolveWith(g, b)
            } else g !== a && g.resolveWith(g, d ? [a] : []);
            return g.promise()
        }
    }), f.support = function () {
        var a = c.createElement("div"),
            b = c.documentElement,
            d, e, f, g, h, i, j, k, l, m, n, o, p, q, r;
        a.setAttribute("className", "t"), a.innerHTML = "   <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>", d = a.getElementsByTagName("*"), e = a.getElementsByTagName("a")[0];
        if (!d || !d.length || !e) return {};
        f = c.createElement("select"), g = f.appendChild(c.createElement("option")), h = a.getElementsByTagName("input")[0], j = {
            leadingWhitespace: a.firstChild.nodeType === 3,
            tbody: !a.getElementsByTagName("tbody").length,
            htmlSerialize: !! a.getElementsByTagName("link").length,
            style: /top/.test(e.getAttribute("style")),
            hrefNormalized: e.getAttribute("href") === "/a",
            opacity: /^0.55$/.test(e.style.opacity),
            cssFloat: !! e.style.cssFloat,
            checkOn: h.value === "on",
            optSelected: g.selected,
            getSetAttribute: a.className !== "t",
            submitBubbles: !0,
            changeBubbles: !0,
            focusinBubbles: !1,
            deleteExpando: !0,
            noCloneEvent: !0,
            inlineBlockNeedsLayout: !1,
            shrinkWrapBlocks: !1,
            reliableMarginRight: !0
        }, h.checked = !0, j.noCloneChecked = h.cloneNode(!0).checked, f.disabled = !0, j.optDisabled = !g.disabled;
        try {
            delete a.test
        } catch (s) {
            j.deleteExpando = !1
        }!a.addEventListener && a.attachEvent && a.fireEvent && (a.attachEvent("onclick", function b() {
            j.noCloneEvent = !1, a.detachEvent("onclick", b)
        }), a.cloneNode(!0).fireEvent("onclick")), h = c.createElement("input"), h.value = "t", h.setAttribute("type", "radio"), j.radioValue = h.value === "t", h.setAttribute("checked", "checked"), a.appendChild(h), k = c.createDocumentFragment(), k.appendChild(a.firstChild), j.checkClone = k.cloneNode(!0).cloneNode(!0).lastChild.checked, a.innerHTML = "", a.style.width = a.style.paddingLeft = "1px", l = c.createElement("body"), m = {
            visibility: "hidden",
            width: 0,
            height: 0,
            border: 0,
            margin: 0,
            background: "none"
        };
        for (q in m) l.style[q] = m[q];
        l.appendChild(a), b.insertBefore(l, b.firstChild), j.appendChecked = h.checked, j.boxModel = a.offsetWidth === 2, "zoom" in a.style && (a.style.display = "inline", a.style.zoom = 1, j.inlineBlockNeedsLayout = a.offsetWidth === 2, a.style.display = "", a.innerHTML = "<div style='width:4px;'></div>", j.shrinkWrapBlocks = a.offsetWidth !== 2), a.innerHTML = "<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>", n = a.getElementsByTagName("td"), r = n[0].offsetHeight === 0, n[0].style.display = "", n[1].style.display = "none", j.reliableHiddenOffsets = r && n[0].offsetHeight === 0, a.innerHTML = "", c.defaultView && c.defaultView.getComputedStyle && (i = c.createElement("div"), i.style.width = "0", i.style.marginRight = "0", a.appendChild(i), j.reliableMarginRight = (parseInt((c.defaultView.getComputedStyle(i, null) || {
            marginRight: 0
        }).marginRight, 10) || 0) === 0), l.innerHTML = "", b.removeChild(l);
        if (a.attachEvent) for (q in {
            submit: 1,
            change: 1,
            focusin: 1
        }) p = "on" + q, r = p in a, r || (a.setAttribute(p, "return;"), r = typeof a[p] == "function"), j[q + "Bubbles"] = r;
        return j
    }(), f.boxModel = f.support.boxModel;
    var i = /^(?:\{.*\}|\[.*\])$/,
        j = /([a-z])([A-Z])/g;
    f.extend({
        cache: {},
        uuid: 0,
        expando: "jQuery" + (f.fn.jquery + Math.random()).replace(/\D/g, ""),
        noData: {
            embed: !0,
            object: "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
            applet: !0
        },
        hasData: function (a) {
            a = a.nodeType ? f.cache[a[f.expando]] : a[f.expando];
            return !!a && !l(a)
        },
        data: function (a, c, d, e) {
            if ( !! f.acceptData(a)) {
                var g = f.expando,
                    h = typeof c == "string",
                    i, j = a.nodeType,
                    k = j ? f.cache : a,
                    l = j ? a[f.expando] : a[f.expando] && f.expando;
                if ((!l || e && l && !k[l][g]) && h && d === b) return;
                l || (j ? a[f.expando] = l = ++f.uuid : l = f.expando), k[l] || (k[l] = {}, j || (k[l].toJSON = f.noop));
                if (typeof c == "object" || typeof c == "function") e ? k[l][g] = f.extend(k[l][g], c) : k[l] = f.extend(k[l], c);
                i = k[l], e && (i[g] || (i[g] = {}), i = i[g]), d !== b && (i[f.camelCase(c)] = d);
                if (c === "events" && !i[c]) return i[g] && i[g].events;
                return h ? i[f.camelCase(c)] : i
            }
        },
        removeData: function (b, c, d) {
            if ( !! f.acceptData(b)) {
                var e = f.expando,
                    g = b.nodeType,
                    h = g ? f.cache : b,
                    i = g ? b[f.expando] : f.expando;
                if (!h[i]) return;
                if (c) {
                    var j = d ? h[i][e] : h[i];
                    if (j) {
                        delete j[c];
                        if (!l(j)) return
                    }
                }
                if (d) {
                    delete h[i][e];
                    if (!l(h[i])) return
                }
                var k = h[i][e];
                f.support.deleteExpando || h != a ? delete h[i] : h[i] = null, k ? (h[i] = {}, g || (h[i].toJSON = f.noop), h[i][e] = k) : g && (f.support.deleteExpando ? delete b[f.expando] : b.removeAttribute ? b.removeAttribute(f.expando) : b[f.expando] = null)
            }
        },
        _data: function (a, b, c) {
            return f.data(a, b, c, !0)
        },
        acceptData: function (a) {
            if (a.nodeName) {
                var b = f.noData[a.nodeName.toLowerCase()];
                if (b) return b !== !0 && a.getAttribute("classid") === b
            }
            return !0
        }
    }), f.fn.extend({
        data: function (a, c) {
            var d = null;
            if (typeof a == "undefined") {
                if (this.length) {
                    d = f.data(this[0]);
                    if (this[0].nodeType === 1) {
                        var e = this[0].attributes,
                            g;
                        for (var h = 0, i = e.length; h < i; h++) g = e[h].name, g.indexOf("data-") === 0 && (g = f.camelCase(g.substring(5)), k(this[0], g, d[g]))
                    }
                }
                return d
            }
            if (typeof a == "object") return this.each(function () {
                f.data(this, a)
            });
            var j = a.split(".");
            j[1] = j[1] ? "." + j[1] : "";
            if (c === b) {
                d = this.triggerHandler("getData" + j[1] + "!", [j[0]]), d === b && this.length && (d = f.data(this[0], a), d = k(this[0], a, d));
                return d === b && j[1] ? this.data(j[0]) : d
            }
            return this.each(function () {
                var b = f(this),
                    d = [j[0], c];
                b.triggerHandler("setData" + j[1] + "!", d), f.data(this, a, c), b.triggerHandler("changeData" + j[1] + "!", d)
            })
        },
        removeData: function (a) {
            return this.each(function () {
                f.removeData(this, a)
            })
        }
    }), f.extend({
        _mark: function (a, c) {
            a && (c = (c || "fx") + "mark", f.data(a, c, (f.data(a, c, b, !0) || 0) + 1, !0))
        },
        _unmark: function (a, c, d) {
            a !== !0 && (d = c, c = a, a = !1);
            if (c) {
                d = d || "fx";
                var e = d + "mark",
                    g = a ? 0 : (f.data(c, e, b, !0) || 1) - 1;
                g ? f.data(c, e, g, !0) : (f.removeData(c, e, !0), m(c, d, "mark"))
            }
        },
        queue: function (a, c, d) {
            if (a) {
                c = (c || "fx") + "queue";
                var e = f.data(a, c, b, !0);
                d && (!e || f.isArray(d) ? e = f.data(a, c, f.makeArray(d), !0) : e.push(d));
                return e || []
            }
        },
        dequeue: function (a, b) {
            b = b || "fx";
            var c = f.queue(a, b),
                d = c.shift(),
                e;
            d === "inprogress" && (d = c.shift()), d && (b === "fx" && c.unshift("inprogress"), d.call(a, function () {
                f.dequeue(a, b)
            })), c.length || (f.removeData(a, b + "queue", !0), m(a, b, "queue"))
        }
    }), f.fn.extend({
        queue: function (a, c) {
            typeof a != "string" && (c = a, a = "fx");
            if (c === b) return f.queue(this[0], a);
            return this.each(function () {
                var b = f.queue(this, a, c);
                a === "fx" && b[0] !== "inprogress" && f.dequeue(this, a)
            })
        },
        dequeue: function (a) {
            return this.each(function () {
                f.dequeue(this, a)
            })
        },
        delay: function (a, b) {
            a = f.fx ? f.fx.speeds[a] || a : a, b = b || "fx";
            return this.queue(b, function () {
                var c = this;
                setTimeout(function () {
                    f.dequeue(c, b)
                }, a)
            })
        },
        clearQueue: function (a) {
            return this.queue(a || "fx", [])
        },
        promise: function (a, c) {
            function m() {
                --h || d.resolveWith(e, [e])
            }
            typeof a != "string" && (c = a, a = b), a = a || "fx";
            var d = f.Deferred(),
                e = this,
                g = e.length,
                h = 1,
                i = a + "defer",
                j = a + "queue",
                k = a + "mark",
                l;
            while (g--) if (l = f.data(e[g], i, b, !0) || (f.data(e[g], j, b, !0) || f.data(e[g], k, b, !0)) && f.data(e[g], i, f._Deferred(), !0)) h++, l.done(m);
            m();
            return d.promise()
        }
    });
    var n = /[\n\t\r]/g,
        o = /\s+/,
        p = /\r/g,
        q = /^(?:button|input)$/i,
        r = /^(?:button|input|object|select|textarea)$/i,
        s = /^a(?:rea)?$/i,
        t = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
        u = /\:/,
        v, w;
    f.fn.extend({
        attr: function (a, b) {
            return f.access(this, a, b, !0, f.attr)
        },
        removeAttr: function (a) {
            return this.each(function () {
                f.removeAttr(this, a)
            })
        },
        prop: function (a, b) {
            return f.access(this, a, b, !0, f.prop)
        },
        removeProp: function (a) {
            a = f.propFix[a] || a;
            return this.each(function () {
                try {
                    this[a] = b, delete this[a]
                } catch (c) {}
            })
        },
        addClass: function (a) {
            if (f.isFunction(a)) return this.each(function (b) {
                var c = f(this);
                c.addClass(a.call(this, b, c.attr("class") || ""))
            });
            if (a && typeof a == "string") {
                var b = (a || "").split(o);
                for (var c = 0, d = this.length; c < d; c++) {
                    var e = this[c];
                    if (e.nodeType === 1) if (!e.className) e.className = a;
                    else {
                        var g = " " + e.className + " ",
                            h = e.className;
                        for (var i = 0, j = b.length; i < j; i++) g.indexOf(" " + b[i] + " ") < 0 && (h += " " + b[i]);
                        e.className = f.trim(h)
                    }
                }
            }
            return this
        },
        removeClass: function (a) {
            if (f.isFunction(a)) return this.each(function (b) {
                var c = f(this);
                c.removeClass(a.call(this, b, c.attr("class")))
            });
            if (a && typeof a == "string" || a === b) {
                var c = (a || "").split(o);
                for (var d = 0, e = this.length; d < e; d++) {
                    var g = this[d];
                    if (g.nodeType === 1 && g.className) if (a) {
                        var h = (" " + g.className + " ").replace(n, " ");
                        for (var i = 0, j = c.length; i < j; i++) h = h.replace(" " + c[i] + " ", " ");
                        g.className = f.trim(h)
                    } else g.className = ""
                }
            }
            return this
        },
        toggleClass: function (a, b) {
            var c = typeof a,
                d = typeof b == "boolean";
            if (f.isFunction(a)) return this.each(function (c) {
                var d = f(this);
                d.toggleClass(a.call(this, c, d.attr("class"), b), b)
            });
            return this.each(function () {
                if (c === "string") {
                    var e, g = 0,
                        h = f(this),
                        i = b,
                        j = a.split(o);
                    while (e = j[g++]) i = d ? i : !h.hasClass(e), h[i ? "addClass" : "removeClass"](e)
                } else if (c === "undefined" || c === "boolean") this.className && f._data(this, "__className__", this.className), this.className = this.className || a === !1 ? "" : f._data(this, "__className__") || ""
            })
        },
        hasClass: function (a) {
            var b = " " + a + " ";
            for (var c = 0, d = this.length; c < d; c++) if ((" " + this[c].className + " ").replace(n, " ").indexOf(b) > -1) return !0;
            return !1
        },
        val: function (a) {
            var c, d, e = this[0];
            if (!arguments.length) {
                if (e) {
                    c = f.valHooks[e.nodeName.toLowerCase()] || f.valHooks[e.type];
                    if (c && "get" in c && (d = c.get(e, "value")) !== b) return d;
                    return (e.value || "").replace(p, "")
                }
                return b
            }
            var g = f.isFunction(a);
            return this.each(function (d) {
                var e = f(this),
                    h;
                if (this.nodeType === 1) {
                    g ? h = a.call(this, d, e.val()) : h = a, h == null ? h = "" : typeof h == "number" ? h += "" : f.isArray(h) && (h = f.map(h, function (a) {
                        return a == null ? "" : a + ""
                    })), c = f.valHooks[this.nodeName.toLowerCase()] || f.valHooks[this.type];
                    if (!c || !("set" in c) || c.set(this, h, "value") === b) this.value = h
                }
            })
        }
    }), f.extend({
        valHooks: {
            option: {
                get: function (a) {
                    var b = a.attributes.value;
                    return !b || b.specified ? a.value : a.text
                }
            },
            select: {
                get: function (a) {
                    var b, c = a.selectedIndex,
                        d = [],
                        e = a.options,
                        g = a.type === "select-one";
                    if (c < 0) return null;
                    for (var h = g ? c : 0, i = g ? c + 1 : e.length; h < i; h++) {
                        var j = e[h];
                        if (j.selected && (f.support.optDisabled ? !j.disabled : j.getAttribute("disabled") === null) && (!j.parentNode.disabled || !f.nodeName(j.parentNode, "optgroup"))) {
                            b = f(j).val();
                            if (g) return b;
                            d.push(b)
                        }
                    }
                    if (g && !d.length && e.length) return f(e[c]).val();
                    return d
                },
                set: function (a, b) {
                    var c = f.makeArray(b);
                    f(a).find("option").each(function () {
                        this.selected = f.inArray(f(this).val(), c) >= 0
                    }), c.length || (a.selectedIndex = -1);
                    return c
                }
            }
        },
        attrFn: {
            val: !0,
            css: !0,
            html: !0,
            text: !0,
            data: !0,
            width: !0,
            height: !0,
            offset: !0
        },
        attrFix: {
            tabindex: "tabIndex"
        },
        attr: function (a, c, d, e) {
            var g = a.nodeType;
            if (!a || g === 3 || g === 8 || g === 2) return b;
            if (e && c in f.attrFn) return f(a)[c](d);
            if (!("getAttribute" in a)) return f.prop(a, c, d);
            var h, i, j = g !== 1 || !f.isXMLDoc(a);
            c = j && f.attrFix[c] || c, i = f.attrHooks[c], i || (!t.test(c) || typeof d != "boolean" && d !== b && d.toLowerCase() !== c.toLowerCase() ? v && (f.nodeName(a, "form") || u.test(c)) && (i = v) : i = w);
            if (d !== b) {
                if (d === null) {
                    f.removeAttr(a, c);
                    return b
                }
                if (i && "set" in i && j && (h = i.set(a, d, c)) !== b) return h;
                a.setAttribute(c, "" + d);
                return d
            }
            if (i && "get" in i && j) return i.get(a, c);
            h = a.getAttribute(c);
            return h === null ? b : h
        },
        removeAttr: function (a, b) {
            var c;
            a.nodeType === 1 && (b = f.attrFix[b] || b, f.support.getSetAttribute ? a.removeAttribute(b) : (f.attr(a, b, ""), a.removeAttributeNode(a.getAttributeNode(b))), t.test(b) && (c = f.propFix[b] || b) in a && (a[c] = !1))
        },
        attrHooks: {
            type: {
                set: function (a, b) {
                    if (q.test(a.nodeName) && a.parentNode) f.error("type property can't be changed");
                    else if (!f.support.radioValue && b === "radio" && f.nodeName(a, "input")) {
                        var c = a.value;
                        a.setAttribute("type", b), c && (a.value = c);
                        return b
                    }
                }
            },
            tabIndex: {
                get: function (a) {
                    var c = a.getAttributeNode("tabIndex");
                    return c && c.specified ? parseInt(c.value, 10) : r.test(a.nodeName) || s.test(a.nodeName) && a.href ? 0 : b
                }
            }
        },
        propFix: {
            tabindex: "tabIndex",
            readonly: "readOnly",
            "for": "htmlFor",
            "class": "className",
            maxlength: "maxLength",
            cellspacing: "cellSpacing",
            cellpadding: "cellPadding",
            rowspan: "rowSpan",
            colspan: "colSpan",
            usemap: "useMap",
            frameborder: "frameBorder",
            contenteditable: "contentEditable"
        },
        prop: function (a, c, d) {
            var e = a.nodeType;
            if (!a || e === 3 || e === 8 || e === 2) return b;
            var g, h, i = e !== 1 || !f.isXMLDoc(a);
            c = i && f.propFix[c] || c, h = f.propHooks[c];
            return d !== b ? h && "set" in h && (g = h.set(a, d, c)) !== b ? g : a[c] = d : h && "get" in h && (g = h.get(a, c)) !== b ? g : a[c]
        },
        propHooks: {}
    }), w = {
        get: function (a, c) {
            return a[f.propFix[c] || c] ? c.toLowerCase() : b
        },
        set: function (a, b, c) {
            var d;
            b === !1 ? f.removeAttr(a, c) : (d = f.propFix[c] || c, d in a && (a[d] = b), a.setAttribute(c, c.toLowerCase()));
            return c
        }
    }, f.attrHooks.value = {
        get: function (a, b) {
            if (v && f.nodeName(a, "button")) return v.get(a, b);
            return a.value
        },
        set: function (a, b, c) {
            if (v && f.nodeName(a, "button")) return v.set(a, b, c);
            a.value = b
        }
    }, f.support.getSetAttribute || (f.attrFix = f.propFix, v = f.attrHooks.name = f.valHooks.button = {
        get: function (a, c) {
            var d;
            d = a.getAttributeNode(c);
            return d && d.nodeValue !== "" ? d.nodeValue : b
        },
        set: function (a, b, c) {
            var d = a.getAttributeNode(c);
            if (d) {
                d.nodeValue = b;
                return b
            }
        }
    }, f.each(["width", "height"], function (a, b) {
        f.attrHooks[b] = f.extend(f.attrHooks[b], {
            set: function (a, c) {
                if (c === "") {
                    a.setAttribute(b, "auto");
                    return c
                }
            }
        })
    })), f.support.hrefNormalized || f.each(["href", "src", "width", "height"], function (a, c) {
        f.attrHooks[c] = f.extend(f.attrHooks[c], {
            get: function (a) {
                var d = a.getAttribute(c, 2);
                return d === null ? b : d
            }
        })
    }), f.support.style || (f.attrHooks.style = {
        get: function (a) {
            return a.style.cssText.toLowerCase() || b
        },
        set: function (a, b) {
            return a.style.cssText = "" + b
        }
    }), f.support.optSelected || (f.propHooks.selected = f.extend(f.propHooks.selected, {
        get: function (a) {
            var b = a.parentNode;
            b && (b.selectedIndex, b.parentNode && b.parentNode.selectedIndex)
        }
    })), f.support.checkOn || f.each(["radio", "checkbox"], function () {
        f.valHooks[this] = {
            get: function (a) {
                return a.getAttribute("value") === null ? "on" : a.value
            }
        }
    }), f.each(["radio", "checkbox"], function () {
        f.valHooks[this] = f.extend(f.valHooks[this], {
            set: function (a, b) {
                if (f.isArray(b)) return a.checked = f.inArray(f(a).val(), b) >= 0
            }
        })
    });
    var x = Object.prototype.hasOwnProperty,
        y = /\.(.*)$/,
        z = /^(?:textarea|input|select)$/i,
        A = /\./g,
        B = / /g,
        C = /[^\w\s.|`]/g,
        D = function (a) {
            return a.replace(C, "\\$&")
        };
    f.event = {
        add: function (a, c, d, e) {
            if (a.nodeType !== 3 && a.nodeType !== 8) {
                if (d === !1) d = E;
                else if (!d) return;
                var g, h;
                d.handler && (g = d, d = g.handler), d.guid || (d.guid = f.guid++);
                var i = f._data(a);
                if (!i) return;
                var j = i.events,
                    k = i.handle;
                j || (i.events = j = {}), k || (i.handle = k = function (a) {
                    return typeof f != "undefined" && (!a || f.event.triggered !== a.type) ? f.event.handle.apply(k.elem, arguments) : b
                }), k.elem = a, c = c.split(" ");
                var l, m = 0,
                    n;
                while (l = c[m++]) {
                    h = g ? f.extend({}, g) : {
                        handler: d,
                        data: e
                    }, l.indexOf(".") > -1 ? (n = l.split("."), l = n.shift(), h.namespace = n.slice(0).sort().join(".")) : (n = [], h.namespace = ""), h.type = l, h.guid || (h.guid = d.guid);
                    var o = j[l],
                        p = f.event.special[l] || {};
                    if (!o) {
                        o = j[l] = [];
                        if (!p.setup || p.setup.call(a, e, n, k) === !1) a.addEventListener ? a.addEventListener(l, k, !1) : a.attachEvent && a.attachEvent("on" + l, k)
                    }
                    p.add && (p.add.call(a, h), h.handler.guid || (h.handler.guid = d.guid)), o.push(h), f.event.global[l] = !0
                }
                a = null
            }
        },
        global: {},
        remove: function (a, c, d, e) {
            if (a.nodeType !== 3 && a.nodeType !== 8) {
                d === !1 && (d = E);
                var g, h, i, j, k = 0,
                    l, m, n, o, p, q, r, s = f.hasData(a) && f._data(a),
                    t = s && s.events;
                if (!s || !t) return;
                c && c.type && (d = c.handler, c = c.type);
                if (!c || typeof c == "string" && c.charAt(0) === ".") {
                    c = c || "";
                    for (h in t) f.event.remove(a, h + c);
                    return
                }
                c = c.split(" ");
                while (h = c[k++]) {
                    r = h, q = null, l = h.indexOf(".") < 0, m = [], l || (m = h.split("."), h = m.shift(), n = new RegExp("(^|\\.)" + f.map(m.slice(0).sort(), D).join("\\.(?:.*\\.)?") + "(\\.|$)")), p = t[h];
                    if (!p) continue;
                    if (!d) {
                        for (j = 0; j < p.length; j++) {
                            q = p[j];
                            if (l || n.test(q.namespace)) f.event.remove(a, r, q.handler, j), p.splice(j--, 1)
                        }
                        continue
                    }
                    o = f.event.special[h] || {};
                    for (j = e || 0; j < p.length; j++) {
                        q = p[j];
                        if (d.guid === q.guid) {
                            if (l || n.test(q.namespace)) e == null && p.splice(j--, 1), o.remove && o.remove.call(a, q);
                            if (e != null) break
                        }
                    }
                    if (p.length === 0 || e != null && p.length === 1)(!o.teardown || o.teardown.call(a, m) === !1) && f.removeEvent(a, h, s.handle), g = null, delete t[h]
                }
                if (f.isEmptyObject(t)) {
                    var u = s.handle;
                    u && (u.elem = null), delete s.events, delete s.handle, f.isEmptyObject(s) && f.removeData(a, b, !0)
                }
            }
        },
        customEvent: {
            getData: !0,
            setData: !0,
            changeData: !0
        },
        trigger: function (c, d, e, g) {
            var h = c.type || c,
                i = [],
                j;
            h.indexOf("!") >= 0 && (h = h.slice(0, -1), j = !0), h.indexOf(".") >= 0 && (i = h.split("."), h = i.shift(), i.sort());
            if ( !! e && !f.event.customEvent[h] || !! f.event.global[h]) {
                c = typeof c == "object" ? c[f.expando] ? c : new f.Event(h, c) : new f.Event(h), c.type = h, c.exclusive = j, c.namespace = i.join("."), c.namespace_re = new RegExp("(^|\\.)" + i.join("\\.(?:.*\\.)?") + "(\\.|$)");
                if (g || !e) c.preventDefault(), c.stopPropagation();
                if (!e) {
                    f.each(f.cache, function () {
                        var a = f.expando,
                            b = this[a];
                        b && b.events && b.events[h] && f.event.trigger(c, d, b.handle.elem)
                    });
                    return
                }
                if (e.nodeType === 3 || e.nodeType === 8) return;
                c.result = b, c.target = e, d = d ? f.makeArray(d) : [], d.unshift(c);
                var k = e,
                    l = h.indexOf(":") < 0 ? "on" + h : "";
                do {
                    var m = f._data(k, "handle");
                    c.currentTarget = k, m && m.apply(k, d), l && f.acceptData(k) && k[l] && k[l].apply(k, d) === !1 && (c.result = !1, c.preventDefault()), k = k.parentNode || k.ownerDocument || k === c.target.ownerDocument && a
                } while (k && !c.isPropagationStopped());
                if (!c.isDefaultPrevented()) {
                    var n, o = f.event.special[h] || {};
                    if ((!o._default || o._default.call(e.ownerDocument, c) === !1) && (h !== "click" || !f.nodeName(e, "a")) && f.acceptData(e)) {
                        try {
                            l && e[h] && (n = e[l], n && (e[l] = null), f.event.triggered = h, e[h]())
                        } catch (p) {}
                        n && (e[l] = n), f.event.triggered = b
                    }
                }
                return c.result
            }
        },
        handle: function (c) {
            c = f.event.fix(c || a.event);
            var d = ((f._data(this, "events") || {})[c.type] || []).slice(0),
                e = !c.exclusive && !c.namespace,
                g = Array.prototype.slice.call(arguments, 0);
            g[0] = c, c.currentTarget = this;
            for (var h = 0, i = d.length; h < i; h++) {
                var j = d[h];
                if (e || c.namespace_re.test(j.namespace)) {
                    c.handler = j.handler, c.data = j.data, c.handleObj = j;
                    var k = j.handler.apply(this, g);
                    k !== b && (c.result = k, k === !1 && (c.preventDefault(), c.stopPropagation()));
                    if (c.isImmediatePropagationStopped()) break
                }
            }
            return c.result
        },
        props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
        fix: function (a) {
            if (a[f.expando]) return a;
            var d = a;
            a = f.Event(d);
            for (var e = this.props.length, g; e;) g = this.props[--e], a[g] = d[g];
            a.target || (a.target = a.srcElement || c), a.target.nodeType === 3 && (a.target = a.target.parentNode), !a.relatedTarget && a.fromElement && (a.relatedTarget = a.fromElement === a.target ? a.toElement : a.fromElement);
            if (a.pageX == null && a.clientX != null) {
                var h = a.target.ownerDocument || c,
                    i = h.documentElement,
                    j = h.body;
                a.pageX = a.clientX + (i && i.scrollLeft || j && j.scrollLeft || 0) - (i && i.clientLeft || j && j.clientLeft || 0), a.pageY = a.clientY + (i && i.scrollTop || j && j.scrollTop || 0) - (i && i.clientTop || j && j.clientTop || 0)
            }
            a.which == null && (a.charCode != null || a.keyCode != null) && (a.which = a.charCode != null ? a.charCode : a.keyCode), !a.metaKey && a.ctrlKey && (a.metaKey = a.ctrlKey), !a.which && a.button !== b && (a.which = a.button & 1 ? 1 : a.button & 2 ? 3 : a.button & 4 ? 2 : 0);
            return a
        },
        guid: 1e8,
        proxy: f.proxy,
        special: {
            ready: {
                setup: f.bindReady,
                teardown: f.noop
            },
            live: {
                add: function (a) {
                    f.event.add(this, O(a.origType, a.selector), f.extend({}, a, {
                        handler: N,
                        guid: a.handler.guid
                    }))
                },
                remove: function (a) {
                    f.event.remove(this, O(a.origType, a.selector), a)
                }
            },
            beforeunload: {
                setup: function (a, b, c) {
                    f.isWindow(this) && (this.onbeforeunload = c)
                },
                teardown: function (a, b) {
                    this.onbeforeunload === b && (this.onbeforeunload = null)
                }
            }
        }
    }, f.removeEvent = c.removeEventListener ?
    function (a, b, c) {
        a.removeEventListener && a.removeEventListener(b, c, !1)
    } : function (a, b, c) {
        a.detachEvent && a.detachEvent("on" + b, c)
    }, f.Event = function (a, b) {
        if (!this.preventDefault) return new f.Event(a, b);
        a && a.type ? (this.originalEvent = a, this.type = a.type, this.isDefaultPrevented = a.defaultPrevented || a.returnValue === !1 || a.getPreventDefault && a.getPreventDefault() ? F : E) : this.type = a, b && f.extend(this, b), this.timeStamp = f.now(), this[f.expando] = !0
    }, f.Event.prototype = {
        preventDefault: function () {
            this.isDefaultPrevented = F;
            var a = this.originalEvent;
            !a || (a.preventDefault ? a.preventDefault() : a.returnValue = !1)
        },
        stopPropagation: function () {
            this.isPropagationStopped = F;
            var a = this.originalEvent;
            !a || (a.stopPropagation && a.stopPropagation(), a.cancelBubble = !0)
        },
        stopImmediatePropagation: function () {
            this.isImmediatePropagationStopped = F, this.stopPropagation()
        },
        isDefaultPrevented: E,
        isPropagationStopped: E,
        isImmediatePropagationStopped: E
    };
    var G = function (a) {
            var b = a.relatedTarget;
            a.type = a.data;
            try {
                if (b && b !== c && !b.parentNode) return;
                while (b && b !== this) b = b.parentNode;
                b !== this && f.event.handle.apply(this, arguments)
            } catch (d) {}
        },
        H = function (a) {
            a.type = a.data, f.event.handle.apply(this, arguments)
        };
    f.each({
        mouseenter: "mouseover",
        mouseleave: "mouseout"
    }, function (a, b) {
        f.event.special[a] = {
            setup: function (c) {
                f.event.add(this, b, c && c.selector ? H : G, a)
            },
            teardown: function (a) {
                f.event.remove(this, b, a && a.selector ? H : G)
            }
        }
    }), f.support.submitBubbles || (f.event.special.submit = {
        setup: function (a, b) {
            if (!f.nodeName(this, "form")) f.event.add(this, "click.specialSubmit", function (a) {
                var b = a.target,
                    c = b.type;
                (c === "submit" || c === "image") && f(b).closest("form").length && L("submit", this, arguments)
            }), f.event.add(this, "keypress.specialSubmit", function (a) {
                var b = a.target,
                    c = b.type;
                (c === "text" || c === "password") && f(b).closest("form").length && a.keyCode === 13 && L("submit", this, arguments)
            });
            else return !1
        },
        teardown: function (a) {
            f.event.remove(this, ".specialSubmit")
        }
    });
    if (!f.support.changeBubbles) {
        var I, J = function (a) {
                var b = a.type,
                    c = a.value;
                b === "radio" || b === "checkbox" ? c = a.checked : b === "select-multiple" ? c = a.selectedIndex > -1 ? f.map(a.options, function (a) {
                    return a.selected
                }).join("-") : "" : f.nodeName(a, "select") && (c = a.selectedIndex);
                return c
            },
            K = function (c) {
                var d = c.target,
                    e, g;
                if ( !! z.test(d.nodeName) && !d.readOnly) {
                    e = f._data(d, "_change_data"), g = J(d), (c.type !== "focusout" || d.type !== "radio") && f._data(d, "_change_data", g);
                    if (e === b || g === e) return;
                    if (e != null || g) c.type = "change", c.liveFired = b, f.event.trigger(c, arguments[1], d)
                }
            };
        f.event.special.change = {
            filters: {
                focusout: K,
                beforedeactivate: K,
                click: function (a) {
                    var b = a.target,
                        c = f.nodeName(b, "input") ? b.type : "";
                    (c === "radio" || c === "checkbox" || f.nodeName(b, "select")) && K.call(this, a)
                },
                keydown: function (a) {
                    var b = a.target,
                        c = f.nodeName(b, "input") ? b.type : "";
                    (a.keyCode === 13 && !f.nodeName(b, "textarea") || a.keyCode === 32 && (c === "checkbox" || c === "radio") || c === "select-multiple") && K.call(this, a)
                },
                beforeactivate: function (a) {
                    var b = a.target;
                    f._data(b, "_change_data", J(b))
                }
            },
            setup: function (a, b) {
                if (this.type === "file") return !1;
                for (var c in I) f.event.add(this, c + ".specialChange", I[c]);
                return z.test(this.nodeName)
            },
            teardown: function (a) {
                f.event.remove(this, ".specialChange");
                return z.test(this.nodeName)
            }
        }, I = f.event.special.change.filters, I.focus = I.beforeactivate
    }
    f.support.focusinBubbles || f.each({
        focus: "focusin",
        blur: "focusout"
    }, function (a, b) {
        function e(a) {
            var c = f.event.fix(a);
            c.type = b, c.originalEvent = {}, f.event.trigger(c, null, c.target), c.isDefaultPrevented() && a.preventDefault()
        }
        var d = 0;
        f.event.special[b] = {
            setup: function () {
                d++ === 0 && c.addEventListener(a, e, !0)
            },
            teardown: function () {
                --d === 0 && c.removeEventListener(a, e, !0)
            }
        }
    }), f.each(["bind", "one"], function (a, c) {
        f.fn[c] = function (a, d, e) {
            var g;
            if (typeof a == "object") {
                for (var h in a) this[c](h, d, a[h], e);
                return this
            }
            if (arguments.length === 2 || d === !1) e = d, d = b;
            c === "one" ? (g = function (a) {
                f(this).unbind(a, g);
                return e.apply(this, arguments)
            }, g.guid = e.guid || f.guid++) : g = e;
            if (a === "unload" && c !== "one") this.one(a, d, e);
            else for (var i = 0, j = this.length; i < j; i++) f.event.add(this[i], a, g, d);
            return this
        }
    }), f.fn.extend({
        unbind: function (a, b) {
            if (typeof a == "object" && !a.preventDefault) for (var c in a) this.unbind(c, a[c]);
            else for (var d = 0, e = this.length; d < e; d++) f.event.remove(this[d], a, b);
            return this
        },
        delegate: function (a, b, c, d) {
            return this.live(b, c, d, a)
        },
        undelegate: function (a, b, c) {
            return arguments.length === 0 ? this.unbind("live") : this.die(b, null, c, a)
        },
        trigger: function (a, b) {
            return this.each(function () {
                f.event.trigger(a, b, this)
            })
        },
        triggerHandler: function (a, b) {
            if (this[0]) return f.event.trigger(a, b, this[0], !0)
        },
        toggle: function (a) {
            var b = arguments,
                c = a.guid || f.guid++,
                d = 0,
                e = function (c) {
                    var e = (f.data(this, "lastToggle" + a.guid) || 0) % d;
                    f.data(this, "lastToggle" + a.guid, e + 1), c.preventDefault();
                    return b[e].apply(this, arguments) || !1
                };
            e.guid = c;
            while (d < b.length) b[d++].guid = c;
            return this.click(e)
        },
        hover: function (a, b) {
            return this.mouseenter(a).mouseleave(b || a)
        }
    });
    var M = {
        focus: "focusin",
        blur: "focusout",
        mouseenter: "mouseover",
        mouseleave: "mouseout"
    };
    f.each(["live", "die"], function (a, c) {
        f.fn[c] = function (a, d, e, g) {
            var h, i = 0,
                j, k, l, m = g || this.selector,
                n = g ? this : f(this.context);
            if (typeof a == "object" && !a.preventDefault) {
                for (var o in a) n[c](o, d, a[o], m);
                return this
            }
            if (c === "die" && !a && g && g.charAt(0) === ".") {
                n.unbind(g);
                return this
            }
            if (d === !1 || f.isFunction(d)) e = d || E, d = b;
            a = (a || "").split(" ");
            while ((h = a[i++]) != null) {
                j = y.exec(h), k = "", j && (k = j[0], h = h.replace(y, ""));
                if (h === "hover") {
                    a.push("mouseenter" + k, "mouseleave" + k);
                    continue
                }
                l = h, M[h] ? (a.push(M[h] + k), h = h + k) : h = (M[h] || h) + k;
                if (c === "live") for (var p = 0, q = n.length; p < q; p++) f.event.add(n[p], "live." + O(h, m), {
                    data: d,
                    selector: m,
                    handler: e,
                    origType: h,
                    origHandler: e,
                    preType: l
                });
                else n.unbind("live." + O(h, m), e)
            }
            return this
        }
    }), f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "), function (a, b) {
        f.fn[b] = function (a, c) {
            c == null && (c = a, a = null);
            return arguments.length > 0 ? this.bind(b, a, c) : this.trigger(b)
        }, f.attrFn && (f.attrFn[b] = !0)
    }), function () {
        function u(a, b, c, d, e, f) {
            for (var g = 0, h = d.length; g < h; g++) {
                var i = d[g];
                if (i) {
                    var j = !1;
                    i = i[a];
                    while (i) {
                        if (i.sizcache === c) {
                            j = d[i.sizset];
                            break
                        }
                        if (i.nodeType === 1) {
                            f || (i.sizcache = c, i.sizset = g);
                            if (typeof b != "string") {
                                if (i === b) {
                                    j = !0;
                                    break
                                }
                            } else if (k.filter(b, [i]).length > 0) {
                                j = i;
                                break
                            }
                        }
                        i = i[a]
                    }
                    d[g] = j
                }
            }
        }
        function t(a, b, c, d, e, f) {
            for (var g = 0, h = d.length; g < h; g++) {
                var i = d[g];
                if (i) {
                    var j = !1;
                    i = i[a];
                    while (i) {
                        if (i.sizcache === c) {
                            j = d[i.sizset];
                            break
                        }
                        i.nodeType === 1 && !f && (i.sizcache = c, i.sizset = g);
                        if (i.nodeName.toLowerCase() === b) {
                            j = i;
                            break
                        }
                        i = i[a]
                    }
                    d[g] = j
                }
            }
        }
        var a = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
            d = 0,
            e = Object.prototype.toString,
            g = !1,
            h = !0,
            i = /\\/g,
            j = /\W/;
        [0, 0].sort(function () {
            h = !1;
            return 0
        });
        var k = function (b, d, f, g) {
                f = f || [], d = d || c;
                var h = d;
                if (d.nodeType !== 1 && d.nodeType !== 9) return [];
                if (!b || typeof b != "string") return f;
                var i, j, n, o, q, r, s, t, u = !0,
                    w = k.isXML(d),
                    x = [],
                    y = b;
                do {
                    a.exec(""), i = a.exec(y);
                    if (i) {
                        y = i[3], x.push(i[1]);
                        if (i[2]) {
                            o = i[3];
                            break
                        }
                    }
                } while (i);
                if (x.length > 1 && m.exec(b)) if (x.length === 2 && l.relative[x[0]]) j = v(x[0] + x[1], d);
                else {
                    j = l.relative[x[0]] ? [d] : k(x.shift(), d);
                    while (x.length) b = x.shift(), l.relative[b] && (b += x.shift()), j = v(b, j)
                } else {
                    !g && x.length > 1 && d.nodeType === 9 && !w && l.match.ID.test(x[0]) && !l.match.ID.test(x[x.length - 1]) && (q = k.find(x.shift(), d, w), d = q.expr ? k.filter(q.expr, q.set)[0] : q.set[0]);
                    if (d) {
                        q = g ? {
                            expr: x.pop(),
                            set: p(g)
                        } : k.find(x.pop(), x.length === 1 && (x[0] === "~" || x[0] === "+") && d.parentNode ? d.parentNode : d, w), j = q.expr ? k.filter(q.expr, q.set) : q.set, x.length > 0 ? n = p(j) : u = !1;
                        while (x.length) r = x.pop(), s = r, l.relative[r] ? s = x.pop() : r = "", s == null && (s = d), l.relative[r](n, s, w)
                    } else n = x = []
                }
                n || (n = j), n || k.error(r || b);
                if (e.call(n) === "[object Array]") if (!u) f.push.apply(f, n);
                else if (d && d.nodeType === 1) for (t = 0; n[t] != null; t++) n[t] && (n[t] === !0 || n[t].nodeType === 1 && k.contains(d, n[t])) && f.push(j[t]);
                else for (t = 0; n[t] != null; t++) n[t] && n[t].nodeType === 1 && f.push(j[t]);
                else p(n, f);
                o && (k(o, h, f, g), k.uniqueSort(f));
                return f
            };
        k.uniqueSort = function (a) {
            if (r) {
                g = h, a.sort(r);
                if (g) for (var b = 1; b < a.length; b++) a[b] === a[b - 1] && a.splice(b--, 1)
            }
            return a
        }, k.matches = function (a, b) {
            return k(a, null, null, b)
        }, k.matchesSelector = function (a, b) {
            return k(b, null, null, [a]).length > 0
        }, k.find = function (a, b, c) {
            var d;
            if (!a) return [];
            for (var e = 0, f = l.order.length; e < f; e++) {
                var g, h = l.order[e];
                if (g = l.leftMatch[h].exec(a)) {
                    var j = g[1];
                    g.splice(1, 1);
                    if (j.substr(j.length - 1) !== "\\") {
                        g[1] = (g[1] || "").replace(i, ""), d = l.find[h](g, b, c);
                        if (d != null) {
                            a = a.replace(l.match[h], "");
                            break
                        }
                    }
                }
            }
            d || (d = typeof b.getElementsByTagName != "undefined" ? b.getElementsByTagName("*") : []);
            return {
                set: d,
                expr: a
            }
        }, k.filter = function (a, c, d, e) {
            var f, g, h = a,
                i = [],
                j = c,
                m = c && c[0] && k.isXML(c[0]);
            while (a && c.length) {
                for (var n in l.filter) if ((f = l.leftMatch[n].exec(a)) != null && f[2]) {
                    var o, p, q = l.filter[n],
                        r = f[1];
                    g = !1, f.splice(1, 1);
                    if (r.substr(r.length - 1) === "\\") continue;
                    j === i && (i = []);
                    if (l.preFilter[n]) {
                        f = l.preFilter[n](f, j, d, i, e, m);
                        if (!f) g = o = !0;
                        else if (f === !0) continue
                    }
                    if (f) for (var s = 0;
                    (p = j[s]) != null; s++) if (p) {
                        o = q(p, f, s, j);
                        var t = e ^ !! o;
                        d && o != null ? t ? g = !0 : j[s] = !1 : t && (i.push(p), g = !0)
                    }
                    if (o !== b) {
                        d || (j = i), a = a.replace(l.match[n], "");
                        if (!g) return [];
                        break
                    }
                }
                if (a === h) if (g == null) k.error(a);
                else break;
                h = a
            }
            return j
        }, k.error = function (a) {
            throw "Syntax error, unrecognized expression: " + a
        };
        var l = k.selectors = {
            order: ["ID", "NAME", "TAG"],
            match: {
                ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
                CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
                NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,
                ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,
                TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
                CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,
                POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
                PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
            },
            leftMatch: {},
            attrMap: {
                "class": "className",
                "for": "htmlFor"
            },
            attrHandle: {
                href: function (a) {
                    return a.getAttribute("href")
                },
                type: function (a) {
                    return a.getAttribute("type")
                }
            },
            relative: {
                "+": function (a, b) {
                    var c = typeof b == "string",
                        d = c && !j.test(b),
                        e = c && !d;
                    d && (b = b.toLowerCase());
                    for (var f = 0, g = a.length, h; f < g; f++) if (h = a[f]) {
                        while ((h = h.previousSibling) && h.nodeType !== 1);
                        a[f] = e || h && h.nodeName.toLowerCase() === b ? h || !1 : h === b
                    }
                    e && k.filter(b, a, !0)
                },
                ">": function (a, b) {
                    var c, d = typeof b == "string",
                        e = 0,
                        f = a.length;
                    if (d && !j.test(b)) {
                        b = b.toLowerCase();
                        for (; e < f; e++) {
                            c = a[e];
                            if (c) {
                                var g = c.parentNode;
                                a[e] = g.nodeName.toLowerCase() === b ? g : !1
                            }
                        }
                    } else {
                        for (; e < f; e++) c = a[e], c && (a[e] = d ? c.parentNode : c.parentNode === b);
                        d && k.filter(b, a, !0)
                    }
                },
                "": function (a, b, c) {
                    var e, f = d++,
                        g = u;
                    typeof b == "string" && !j.test(b) && (b = b.toLowerCase(), e = b, g = t), g("parentNode", b, f, a, e, c)
                },
                "~": function (a, b, c) {
                    var e, f = d++,
                        g = u;
                    typeof b == "string" && !j.test(b) && (b = b.toLowerCase(), e = b, g = t), g("previousSibling", b, f, a, e, c)
                }
            },
            find: {
                ID: function (a, b, c) {
                    if (typeof b.getElementById != "undefined" && !c) {
                        var d = b.getElementById(a[1]);
                        return d && d.parentNode ? [d] : []
                    }
                },
                NAME: function (a, b) {
                    if (typeof b.getElementsByName != "undefined") {
                        var c = [],
                            d = b.getElementsByName(a[1]);
                        for (var e = 0, f = d.length; e < f; e++) d[e].getAttribute("name") === a[1] && c.push(d[e]);
                        return c.length === 0 ? null : c
                    }
                },
                TAG: function (a, b) {
                    if (typeof b.getElementsByTagName != "undefined") return b.getElementsByTagName(a[1])
                }
            },
            preFilter: {
                CLASS: function (a, b, c, d, e, f) {
                    a = " " + a[1].replace(i, "") + " ";
                    if (f) return a;
                    for (var g = 0, h;
                    (h = b[g]) != null; g++) h && (e ^ (h.className && (" " + h.className + " ").replace(/[\t\n\r]/g, " ").indexOf(a) >= 0) ? c || d.push(h) : c && (b[g] = !1));
                    return !1
                },
                ID: function (a) {
                    return a[1].replace(i, "")
                },
                TAG: function (a, b) {
                    return a[1].replace(i, "").toLowerCase()
                },
                CHILD: function (a) {
                    if (a[1] === "nth") {
                        a[2] || k.error(a[0]), a[2] = a[2].replace(/^\+|\s*/g, "");
                        var b = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2] === "even" && "2n" || a[2] === "odd" && "2n+1" || !/\D/.test(a[2]) && "0n+" + a[2] || a[2]);
                        a[2] = b[1] + (b[2] || 1) - 0, a[3] = b[3] - 0
                    } else a[2] && k.error(a[0]);
                    a[0] = d++;
                    return a
                },
                ATTR: function (a, b, c, d, e, f) {
                    var g = a[1] = a[1].replace(i, "");
                    !f && l.attrMap[g] && (a[1] = l.attrMap[g]), a[4] = (a[4] || a[5] || "").replace(i, ""), a[2] === "~=" && (a[4] = " " + a[4] + " ");
                    return a
                },
                PSEUDO: function (b, c, d, e, f) {
                    if (b[1] === "not") if ((a.exec(b[3]) || "").length > 1 || /^\w/.test(b[3])) b[3] = k(b[3], null, null, c);
                    else {
                        var g = k.filter(b[3], c, d, !0 ^ f);
                        d || e.push.apply(e, g);
                        return !1
                    } else if (l.match.POS.test(b[0]) || l.match.CHILD.test(b[0])) return !0;
                    return b
                },
                POS: function (a) {
                    a.unshift(!0);
                    return a
                }
            },
            filters: {
                enabled: function (a) {
                    return a.disabled === !1 && a.type !== "hidden"
                },
                disabled: function (a) {
                    return a.disabled === !0
                },
                checked: function (a) {
                    return a.checked === !0
                },
                selected: function (a) {
                    a.parentNode && a.parentNode.selectedIndex;
                    return a.selected === !0
                },
                parent: function (a) {
                    return !!a.firstChild
                },
                empty: function (a) {
                    return !a.firstChild
                },
                has: function (a, b, c) {
                    return !!k(c[3], a).length
                },
                header: function (a) {
                    return /h\d/i.test(a.nodeName)
                },
                text: function (a) {
                    var b = a.getAttribute("type"),
                        c = a.type;
                    return a.nodeName.toLowerCase() === "input" && "text" === c && (b === c || b === null)
                },
                radio: function (a) {
                    return a.nodeName.toLowerCase() === "input" && "radio" === a.type
                },
                checkbox: function (a) {
                    return a.nodeName.toLowerCase() === "input" && "checkbox" === a.type
                },
                file: function (a) {
                    return a.nodeName.toLowerCase() === "input" && "file" === a.type
                },
                password: function (a) {
                    return a.nodeName.toLowerCase() === "input" && "password" === a.type
                },
                submit: function (a) {
                    var b = a.nodeName.toLowerCase();
                    return (b === "input" || b === "button") && "submit" === a.type
                },
                image: function (a) {
                    return a.nodeName.toLowerCase() === "input" && "image" === a.type
                },
                reset: function (a) {
                    var b = a.nodeName.toLowerCase();
                    return (b === "input" || b === "button") && "reset" === a.type
                },
                button: function (a) {
                    var b = a.nodeName.toLowerCase();
                    return b === "input" && "button" === a.type || b === "button"
                },
                input: function (a) {
                    return /input|select|textarea|button/i.test(a.nodeName)
                },
                focus: function (a) {
                    return a === a.ownerDocument.activeElement
                }
            },
            setFilters: {
                first: function (a, b) {
                    return b === 0
                },
                last: function (a, b, c, d) {
                    return b === d.length - 1
                },
                even: function (a, b) {
                    return b % 2 === 0
                },
                odd: function (a, b) {
                    return b % 2 === 1
                },
                lt: function (a, b, c) {
                    return b < c[3] - 0
                },
                gt: function (a, b, c) {
                    return b > c[3] - 0
                },
                nth: function (a, b, c) {
                    return c[3] - 0 === b
                },
                eq: function (a, b, c) {
                    return c[3] - 0 === b
                }
            },
            filter: {
                PSEUDO: function (a, b, c, d) {
                    var e = b[1],
                        f = l.filters[e];
                    if (f) return f(a, c, b, d);
                    if (e === "contains") return (a.textContent || a.innerText || k.getText([a]) || "").indexOf(b[3]) >= 0;
                    if (e === "not") {
                        var g = b[3];
                        for (var h = 0, i = g.length; h < i; h++) if (g[h] === a) return !1;
                        return !0
                    }
                    k.error(e)
                },
                CHILD: function (a, b) {
                    var c = b[1],
                        d = a;
                    switch (c) {
                    case "only":
                    case "first":
                        while (d = d.previousSibling) if (d.nodeType === 1) return !1;
                        if (c === "first") return !0;
                        d = a;
                    case "last":
                        while (d = d.nextSibling) if (d.nodeType === 1) return !1;
                        return !0;
                    case "nth":
                        var e = b[2],
                            f = b[3];
                        if (e === 1 && f === 0) return !0;
                        var g = b[0],
                            h = a.parentNode;
                        if (h && (h.sizcache !== g || !a.nodeIndex)) {
                            var i = 0;
                            for (d = h.firstChild; d; d = d.nextSibling) d.nodeType === 1 && (d.nodeIndex = ++i);
                            h.sizcache = g
                        }
                        var j = a.nodeIndex - f;
                        return e === 0 ? j === 0 : j % e === 0 && j / e >= 0
                    }
                },
                ID: function (a, b) {
                    return a.nodeType === 1 && a.getAttribute("id") === b
                },
                TAG: function (a, b) {
                    return b === "*" && a.nodeType === 1 || a.nodeName.toLowerCase() === b
                },
                CLASS: function (a, b) {
                    return (" " + (a.className || a.getAttribute("class")) + " ").indexOf(b) > -1
                },
                ATTR: function (a, b) {
                    var c = b[1],
                        d = l.attrHandle[c] ? l.attrHandle[c](a) : a[c] != null ? a[c] : a.getAttribute(c),
                        e = d + "",
                        f = b[2],
                        g = b[4];
                    return d == null ? f === "!=" : f === "=" ? e === g : f === "*=" ? e.indexOf(g) >= 0 : f === "~=" ? (" " + e + " ").indexOf(g) >= 0 : g ? f === "!=" ? e !== g : f === "^=" ? e.indexOf(g) === 0 : f === "$=" ? e.substr(e.length - g.length) === g : f === "|=" ? e === g || e.substr(0, g.length + 1) === g + "-" : !1 : e && d !== !1
                },
                POS: function (a, b, c, d) {
                    var e = b[2],
                        f = l.setFilters[e];
                    if (f) return f(a, c, b, d)
                }
            }
        },
            m = l.match.POS,
            n = function (a, b) {
                return "\\" + (b - 0 + 1)
            };
        for (var o in l.match) l.match[o] = new RegExp(l.match[o].source + /(?![^\[]*\])(?![^\(]*\))/.source), l.leftMatch[o] = new RegExp(/(^(?:.|\r|\n)*?)/.source + l.match[o].source.replace(/\\(\d+)/g, n));
        var p = function (a, b) {
                a = Array.prototype.slice.call(a, 0);
                if (b) {
                    b.push.apply(b, a);
                    return b
                }
                return a
            };
        try {
            Array.prototype.slice.call(c.documentElement.childNodes, 0)[0].nodeType
        } catch (q) {
            p = function (a, b) {
                var c = 0,
                    d = b || [];
                if (e.call(a) === "[object Array]") Array.prototype.push.apply(d, a);
                else if (typeof a.length == "number") for (var f = a.length; c < f; c++) d.push(a[c]);
                else for (; a[c]; c++) d.push(a[c]);
                return d
            }
        }
        var r, s;
        c.documentElement.compareDocumentPosition ? r = function (a, b) {
            if (a === b) {
                g = !0;
                return 0
            }
            if (!a.compareDocumentPosition || !b.compareDocumentPosition) return a.compareDocumentPosition ? -1 : 1;
            return a.compareDocumentPosition(b) & 4 ? -1 : 1
        } : (r = function (a, b) {
            if (a === b) {
                g = !0;
                return 0
            }
            if (a.sourceIndex && b.sourceIndex) return a.sourceIndex - b.sourceIndex;
            var c, d, e = [],
                f = [],
                h = a.parentNode,
                i = b.parentNode,
                j = h;
            if (h === i) return s(a, b);
            if (!h) return -1;
            if (!i) return 1;
            while (j) e.unshift(j), j = j.parentNode;
            j = i;
            while (j) f.unshift(j), j = j.parentNode;
            c = e.length, d = f.length;
            for (var k = 0; k < c && k < d; k++) if (e[k] !== f[k]) return s(e[k], f[k]);
            return k === c ? s(a, f[k], -1) : s(e[k], b, 1)
        }, s = function (a, b, c) {
            if (a === b) return c;
            var d = a.nextSibling;
            while (d) {
                if (d === b) return -1;
                d = d.nextSibling
            }
            return 1
        }), k.getText = function (a) {
            var b = "",
                c;
            for (var d = 0; a[d]; d++) c = a[d], c.nodeType === 3 || c.nodeType === 4 ? b += c.nodeValue : c.nodeType !== 8 && (b += k.getText(c.childNodes));
            return b
        }, function () {
            var a = c.createElement("div"),
                d = "script" + (new Date).getTime(),
                e = c.documentElement;
            a.innerHTML = "<a name='" + d + "'/>", e.insertBefore(a, e.firstChild), c.getElementById(d) && (l.find.ID = function (a, c, d) {
                if (typeof c.getElementById != "undefined" && !d) {
                    var e = c.getElementById(a[1]);
                    return e ? e.id === a[1] || typeof e.getAttributeNode != "undefined" && e.getAttributeNode("id").nodeValue === a[1] ? [e] : b : []
                }
            }, l.filter.ID = function (a, b) {
                var c = typeof a.getAttributeNode != "undefined" && a.getAttributeNode("id");
                return a.nodeType === 1 && c && c.nodeValue === b
            }), e.removeChild(a), e = a = null
        }(), function () {
            var a = c.createElement("div");
            a.appendChild(c.createComment("")), a.getElementsByTagName("*").length > 0 && (l.find.TAG = function (a, b) {
                var c = b.getElementsByTagName(a[1]);
                if (a[1] === "*") {
                    var d = [];
                    for (var e = 0; c[e]; e++) c[e].nodeType === 1 && d.push(c[e]);
                    c = d
                }
                return c
            }), a.innerHTML = "<a href='#'></a>", a.firstChild && typeof a.firstChild.getAttribute != "undefined" && a.firstChild.getAttribute("href") !== "#" && (l.attrHandle.href = function (a) {
                return a.getAttribute("href", 2)
            }), a = null
        }(), c.querySelectorAll &&
        function () {
            var a = k,
                b = c.createElement("div"),
                d = "__sizzle__";
            b.innerHTML = "<p class='TEST'></p>";
            if (!b.querySelectorAll || b.querySelectorAll(".TEST").length !== 0) {
                k = function (b, e, f, g) {
                    e = e || c;
                    if (!g && !k.isXML(e)) {
                        var h = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);
                        if (h && (e.nodeType === 1 || e.nodeType === 9)) {
                            if (h[1]) return p(e.getElementsByTagName(b), f);
                            if (h[2] && l.find.CLASS && e.getElementsByClassName) return p(e.getElementsByClassName(h[2]), f)
                        }
                        if (e.nodeType === 9) {
                            if (b === "body" && e.body) return p([e.body], f);
                            if (h && h[3]) {
                                var i = e.getElementById(h[3]);
                                if (!i || !i.parentNode) return p([], f);
                                if (i.id === h[3]) return p([i], f)
                            }
                            try {
                                return p(e.querySelectorAll(b), f)
                            } catch (j) {}
                        } else if (e.nodeType === 1 && e.nodeName.toLowerCase() !== "object") {
                            var m = e,
                                n = e.getAttribute("id"),
                                o = n || d,
                                q = e.parentNode,
                                r = /^\s*[+~]/.test(b);
                            n ? o = o.replace(/'/g, "\\$&") : e.setAttribute("id", o), r && q && (e = e.parentNode);
                            try {
                                if (!r || q) return p(e.querySelectorAll("[id='" + o + "'] " + b), f)
                            } catch (s) {} finally {
                                n || m.removeAttribute("id")
                            }
                        }
                    }
                    return a(b, e, f, g)
                };
                for (var e in a) k[e] = a[e];
                b = null
            }
        }(), function () {
            var a = c.documentElement,
                b = a.matchesSelector || a.mozMatchesSelector || a.webkitMatchesSelector || a.msMatchesSelector;
            if (b) {
                var d = !b.call(c.createElement("div"), "div"),
                    e = !1;
                try {
                    b.call(c.documentElement, "[test!='']:sizzle")
                } catch (f) {
                    e = !0
                }
                k.matchesSelector = function (a, c) {
                    c = c.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");
                    if (!k.isXML(a)) try {
                        if (e || !l.match.PSEUDO.test(c) && !/!=/.test(c)) {
                            var f = b.call(a, c);
                            if (f || !d || a.document && a.document.nodeType !== 11) return f
                        }
                    } catch (g) {}
                    return k(c, null, null, [a]).length > 0
                }
            }
        }(), function () {
            var a = c.createElement("div");
            a.innerHTML = "<div class='test e'></div><div class='test'></div>";
            if ( !! a.getElementsByClassName && a.getElementsByClassName("e").length !== 0) {
                a.lastChild.className = "e";
                if (a.getElementsByClassName("e").length === 1) return;
                l.order.splice(1, 0, "CLASS"), l.find.CLASS = function (a, b, c) {
                    if (typeof b.getElementsByClassName != "undefined" && !c) return b.getElementsByClassName(a[1])
                }, a = null
            }
        }(), c.documentElement.contains ? k.contains = function (a, b) {
            return a !== b && (a.contains ? a.contains(b) : !0)
        } : c.documentElement.compareDocumentPosition ? k.contains = function (a, b) {
            return !!(a.compareDocumentPosition(b) & 16)
        } : k.contains = function () {
            return !1
        }, k.isXML = function (a) {
            var b = (a ? a.ownerDocument || a : 0).documentElement;
            return b ? b.nodeName !== "HTML" : !1
        };
        var v = function (a, b) {
                var c, d = [],
                    e = "",
                    f = b.nodeType ? [b] : b;
                while (c = l.match.PSEUDO.exec(a)) e += c[0], a = a.replace(l.match.PSEUDO, "");
                a = l.relative[a] ? a + "*" : a;
                for (var g = 0, h = f.length; g < h; g++) k(a, f[g], d);
                return k.filter(e, d)
            };
        f.find = k, f.expr = k.selectors, f.expr[":"] = f.expr.filters, f.unique = k.uniqueSort, f.text = k.getText, f.isXMLDoc = k.isXML, f.contains = k.contains
    }();
    var P = /Until$/,
        Q = /^(?:parents|prevUntil|prevAll)/,
        R = /,/,
        S = /^.[^:#\[\.,]*$/,
        T = Array.prototype.slice,
        U = f.expr.match.POS,
        V = {
            children: !0,
            contents: !0,
            next: !0,
            prev: !0
        };
    f.fn.extend({
        find: function (a) {
            var b = this,
                c, d;
            if (typeof a != "string") return f(a).filter(function () {
                for (c = 0, d = b.length; c < d; c++) if (f.contains(b[c], this)) return !0
            });
            var e = this.pushStack("", "find", a),
                g, h, i;
            for (c = 0, d = this.length; c < d; c++) {
                g = e.length, f.find(a, this[c], e);
                if (c > 0) for (h = g; h < e.length; h++) for (i = 0; i < g; i++) if (e[i] === e[h]) {
                    e.splice(h--, 1);
                    break
                }
            }
            return e
        },
        has: function (a) {
            var b = f(a);
            return this.filter(function () {
                for (var a = 0, c = b.length; a < c; a++) if (f.contains(this, b[a])) return !0
            })
        },
        not: function (a) {
            return this.pushStack(X(this, a, !1), "not", a)
        },
        filter: function (a) {
            return this.pushStack(X(this, a, !0), "filter", a)
        },
        is: function (a) {
            return !!a && (typeof a == "string" ? f.filter(a, this).length > 0 : this.filter(a).length > 0)
        },
        closest: function (a, b) {
            var c = [],
                d, e, g = this[0];
            if (f.isArray(a)) {
                var h, i, j = {},
                    k = 1;
                if (g && a.length) {
                    for (d = 0, e = a.length; d < e; d++) i = a[d], j[i] || (j[i] = U.test(i) ? f(i, b || this.context) : i);
                    while (g && g.ownerDocument && g !== b) {
                        for (i in j) h = j[i], (h.jquery ? h.index(g) > -1 : f(g).is(h)) && c.push({
                            selector: i,
                            elem: g,
                            level: k
                        });
                        g = g.parentNode, k++
                    }
                }
                return c
            }
            var l = U.test(a) || typeof a != "string" ? f(a, b || this.context) : 0;
            for (d = 0, e = this.length; d < e; d++) {
                g = this[d];
                while (g) {
                    if (l ? l.index(g) > -1 : f.find.matchesSelector(g, a)) {
                        c.push(g);
                        break
                    }
                    g = g.parentNode;
                    if (!g || !g.ownerDocument || g === b || g.nodeType === 11) break
                }
            }
            c = c.length > 1 ? f.unique(c) : c;
            return this.pushStack(c, "closest", a)
        },
        index: function (a) {
            if (!a || typeof a == "string") return f.inArray(this[0], a ? f(a) : this.parent().children());
            return f.inArray(a.jquery ? a[0] : a, this)
        },
        add: function (a, b) {
            var c = typeof a == "string" ? f(a, b) : f.makeArray(a && a.nodeType ? [a] : a),
                d = f.merge(this.get(), c);
            return this.pushStack(W(c[0]) || W(d[0]) ? d : f.unique(d))
        },
        andSelf: function () {
            return this.add(this.prevObject)
        }
    }), f.each({
        parent: function (a) {
            var b = a.parentNode;
            return b && b.nodeType !== 11 ? b : null
        },
        parents: function (a) {
            return f.dir(a, "parentNode")
        },
        parentsUntil: function (a, b, c) {
            return f.dir(a, "parentNode", c)
        },
        next: function (a) {
            return f.nth(a, 2, "nextSibling")
        },
        prev: function (a) {
            return f.nth(a, 2, "previousSibling")
        },
        nextAll: function (a) {
            return f.dir(a, "nextSibling")
        },
        prevAll: function (a) {
            return f.dir(a, "previousSibling")
        },
        nextUntil: function (a, b, c) {
            return f.dir(a, "nextSibling", c)
        },
        prevUntil: function (a, b, c) {
            return f.dir(a, "previousSibling", c)
        },
        siblings: function (a) {
            return f.sibling(a.parentNode.firstChild, a)
        },
        children: function (a) {
            return f.sibling(a.firstChild)
        },
        contents: function (a) {
            return f.nodeName(a, "iframe") ? a.contentDocument || a.contentWindow.document : f.makeArray(a.childNodes)
        }
    }, function (a, b) {
        f.fn[a] = function (c, d) {
            var e = f.map(this, b, c),
                g = T.call(arguments);
            P.test(a) || (d = c), d && typeof d == "string" && (e = f.filter(d, e)), e = this.length > 1 && !V[a] ? f.unique(e) : e, (this.length > 1 || R.test(d)) && Q.test(a) && (e = e.reverse());
            return this.pushStack(e, a, g.join(","))
        }
    }), f.extend({
        filter: function (a, b, c) {
            c && (a = ":not(" + a + ")");
            return b.length === 1 ? f.find.matchesSelector(b[0], a) ? [b[0]] : [] : f.find.matches(a, b)
        },
        dir: function (a, c, d) {
            var e = [],
                g = a[c];
            while (g && g.nodeType !== 9 && (d === b || g.nodeType !== 1 || !f(g).is(d))) g.nodeType === 1 && e.push(g), g = g[c];
            return e
        },
        nth: function (a, b, c, d) {
            b = b || 1;
            var e = 0;
            for (; a; a = a[c]) if (a.nodeType === 1 && ++e === b) break;
            return a
        },
        sibling: function (a, b) {
            var c = [];
            for (; a; a = a.nextSibling) a.nodeType === 1 && a !== b && c.push(a);
            return c
        }
    });
    var Y = / jQuery\d+="(?:\d+|null)"/g,
        Z = /^\s+/,
        $ = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
        _ = /<([\w:]+)/,
        ba = /<tbody/i,
        bb = /<|&#?\w+;/,
        bc = /<(?:script|object|embed|option|style)/i,
        bd = /checked\s*(?:[^=]|=\s*.checked.)/i,
        be = /\/(java|ecma)script/i,
        bf = /^\s*<!(?:\[CDATA\[|\-\-)/,
        bg = {
            option: [1, "<select multiple='multiple'>", "</select>"],
            legend: [1, "<fieldset>", "</fieldset>"],
            thead: [1, "<table>", "</table>"],
            tr: [2, "<table><tbody>", "</tbody></table>"],
            td: [3, "<table><tbody><tr>", "</tr></tbody></table>"],
            col: [2, "<table><tbody></tbody><colgroup>", "</colgroup></table>"],
            area: [1, "<map>", "</map>"],
            _default: [0, "", ""]
        };
    bg.optgroup = bg.option, bg.tbody = bg.tfoot = bg.colgroup = bg.caption = bg.thead, bg.th = bg.td, f.support.htmlSerialize || (bg._default = [1, "div<div>", "</div>"]), f.fn.extend({
        text: function (a) {
            if (f.isFunction(a)) return this.each(function (b) {
                var c = f(this);
                c.text(a.call(this, b, c.text()))
            });
            if (typeof a != "object" && a !== b) return this.empty().append((this[0] && this[0].ownerDocument || c).createTextNode(a));
            return f.text(this)
        },
        wrapAll: function (a) {
            if (f.isFunction(a)) return this.each(function (b) {
                f(this).wrapAll(a.call(this, b))
            });
            if (this[0]) {
                var b = f(a, this[0].ownerDocument).eq(0).clone(!0);
                this[0].parentNode && b.insertBefore(this[0]), b.map(function () {
                    var a = this;
                    while (a.firstChild && a.firstChild.nodeType === 1) a = a.firstChild;
                    return a
                }).append(this)
            }
            return this
        },
        wrapInner: function (a) {
            if (f.isFunction(a)) return this.each(function (b) {
                f(this).wrapInner(a.call(this, b))
            });
            return this.each(function () {
                var b = f(this),
                    c = b.contents();
                c.length ? c.wrapAll(a) : b.append(a)
            })
        },
        wrap: function (a) {
            return this.each(function () {
                f(this).wrapAll(a)
            })
        },
        unwrap: function () {
            return this.parent().each(function () {
                f.nodeName(this, "body") || f(this).replaceWith(this.childNodes)
            }).end()
        },
        append: function () {
            return this.domManip(arguments, !0, function (a) {
                this.nodeType === 1 && this.appendChild(a)
            })
        },
        prepend: function () {
            return this.domManip(arguments, !0, function (a) {
                this.nodeType === 1 && this.insertBefore(a, this.firstChild)
            })
        },
        before: function () {
            if (this[0] && this[0].parentNode) return this.domManip(arguments, !1, function (a) {
                this.parentNode.insertBefore(a, this)
            });
            if (arguments.length) {
                var a = f(arguments[0]);
                a.push.apply(a, this.toArray());
                return this.pushStack(a, "before", arguments)
            }
        },
        after: function () {
            if (this[0] && this[0].parentNode) return this.domManip(arguments, !1, function (a) {
                this.parentNode.insertBefore(a, this.nextSibling)
            });
            if (arguments.length) {
                var a = this.pushStack(this, "after", arguments);
                a.push.apply(a, f(arguments[0]).toArray());
                return a
            }
        },
        remove: function (a, b) {
            for (var c = 0, d;
            (d = this[c]) != null; c++) if (!a || f.filter(a, [d]).length)!b && d.nodeType === 1 && (f.cleanData(d.getElementsByTagName("*")), f.cleanData([d])), d.parentNode && d.parentNode.removeChild(d);
            return this
        },
        empty: function () {
            for (var a = 0, b;
            (b = this[a]) != null; a++) {
                b.nodeType === 1 && f.cleanData(b.getElementsByTagName("*"));
                while (b.firstChild) b.removeChild(b.firstChild)
            }
            return this
        },
        clone: function (a, b) {
            a = a == null ? !1 : a, b = b == null ? a : b;
            return this.map(function () {
                return f.clone(this, a, b)
            })
        },
        html: function (a) {
            if (a === b) return this[0] && this[0].nodeType === 1 ? this[0].innerHTML.replace(Y, "") : null;
            if (typeof a == "string" && !bc.test(a) && (f.support.leadingWhitespace || !Z.test(a)) && !bg[(_.exec(a) || ["", ""])[1].toLowerCase()]) {
                a = a.replace($, "<$1></$2>");
                try {
                    for (var c = 0, d = this.length; c < d; c++) this[c].nodeType === 1 && (f.cleanData(this[c].getElementsByTagName("*")), this[c].innerHTML = a)
                } catch (e) {
                    this.empty().append(a)
                }
            } else f.isFunction(a) ? this.each(function (b) {
                var c = f(this);
                c.html(a.call(this, b, c.html()))
            }) : this.empty().append(a);
            return this
        },
        replaceWith: function (a) {
            if (this[0] && this[0].parentNode) {
                if (f.isFunction(a)) return this.each(function (b) {
                    var c = f(this),
                        d = c.html();
                    c.replaceWith(a.call(this, b, d))
                });
                typeof a != "string" && (a = f(a).detach());
                return this.each(function () {
                    var b = this.nextSibling,
                        c = this.parentNode;
                    f(this).remove(), b ? f(b).before(a) : f(c).append(a)
                })
            }
            return this.length ? this.pushStack(f(f.isFunction(a) ? a() : a), "replaceWith", a) : this
        },
        detach: function (a) {
            return this.remove(a, !0)
        },
        domManip: function (a, c, d) {
            var e, g, h, i, j = a[0],
                k = [];
            if (!f.support.checkClone && arguments.length === 3 && typeof j == "string" && bd.test(j)) return this.each(function () {
                f(this).domManip(a, c, d, !0)
            });
            if (f.isFunction(j)) return this.each(function (e) {
                var g = f(this);
                a[0] = j.call(this, e, c ? g.html() : b), g.domManip(a, c, d)
            });
            if (this[0]) {
                i = j && j.parentNode, f.support.parentNode && i && i.nodeType === 11 && i.childNodes.length === this.length ? e = {
                    fragment: i
                } : e = f.buildFragment(a, this, k), h = e.fragment, h.childNodes.length === 1 ? g = h = h.firstChild : g = h.firstChild;
                if (g) {
                    c = c && f.nodeName(g, "tr");
                    for (var l = 0, m = this.length, n = m - 1; l < m; l++) d.call(c ? bh(this[l], g) : this[l], e.cacheable || m > 1 && l < n ? f.clone(h, !0, !0) : h)
                }
                k.length && f.each(k, bn)
            }
            return this
        }
    }), f.buildFragment = function (a, b, d) {
        var e, g, h, i = b && b[0] ? b[0].ownerDocument || b[0] : c;
        a.length === 1 && typeof a[0] == "string" && a[0].length < 512 && i === c && a[0].charAt(0) === "<" && !bc.test(a[0]) && (f.support.checkClone || !bd.test(a[0])) && (g = !0, h = f.fragments[a[0]], h && h !== 1 && (e = h)), e || (e = i.createDocumentFragment(), f.clean(a, i, e, d)), g && (f.fragments[a[0]] = h ? e : 1);
        return {
            fragment: e,
            cacheable: g
        }
    }, f.fragments = {}, f.each({
        appendTo: "append",
        prependTo: "prepend",
        insertBefore: "before",
        insertAfter: "after",
        replaceAll: "replaceWith"
    }, function (a, b) {
        f.fn[a] = function (c) {
            var d = [],
                e = f(c),
                g = this.length === 1 && this[0].parentNode;
            if (g && g.nodeType === 11 && g.childNodes.length === 1 && e.length === 1) {
                e[b](this[0]);
                return this
            }
            for (var h = 0, i = e.length; h < i; h++) {
                var j = (h > 0 ? this.clone(!0) : this).get();
                f(e[h])[b](j), d = d.concat(j)
            }
            return this.pushStack(d, a, e.selector)
        }
    }), f.extend({
        clone: function (a, b, c) {
            var d = a.cloneNode(!0),
                e, g, h;
            if ((!f.support.noCloneEvent || !f.support.noCloneChecked) && (a.nodeType === 1 || a.nodeType === 11) && !f.isXMLDoc(a)) {
                bj(a, d), e = bk(a), g = bk(d);
                for (h = 0; e[h]; ++h) bj(e[h], g[h])
            }
            if (b) {
                bi(a, d);
                if (c) {
                    e = bk(a), g = bk(d);
                    for (h = 0; e[h]; ++h) bi(e[h], g[h])
                }
            }
            return d
        },
        clean: function (a, b, d, e) {
            var g;
            b = b || c, typeof b.createElement == "undefined" && (b = b.ownerDocument || b[0] && b[0].ownerDocument || c);
            var h = [],
                i;
            for (var j = 0, k;
            (k = a[j]) != null; j++) {
                typeof k == "number" && (k += "");
                if (!k) continue;
                if (typeof k == "string") if (!bb.test(k)) k = b.createTextNode(k);
                else {
                    k = k.replace($, "<$1></$2>");
                    var l = (_.exec(k) || ["", ""])[1].toLowerCase(),
                        m = bg[l] || bg._default,
                        n = m[0],
                        o = b.createElement("div");
                    o.innerHTML = m[1] + k + m[2];
                    while (n--) o = o.lastChild;
                    if (!f.support.tbody) {
                        var p = ba.test(k),
                            q = l === "table" && !p ? o.firstChild && o.firstChild.childNodes : m[1] === "<table>" && !p ? o.childNodes : [];
                        for (i = q.length - 1; i >= 0; --i) f.nodeName(q[i], "tbody") && !q[i].childNodes.length && q[i].parentNode.removeChild(q[i])
                    }!f.support.leadingWhitespace && Z.test(k) && o.insertBefore(b.createTextNode(Z.exec(k)[0]), o.firstChild), k = o.childNodes
                }
                var r;
                if (!f.support.appendChecked) if (k[0] && typeof (r = k.length) == "number") for (i = 0; i < r; i++) bm(k[i]);
                else bm(k);
                k.nodeType ? h.push(k) : h = f.merge(h, k)
            }
            if (d) {
                g = function (a) {
                    return !a.type || be.test(a.type)
                };
                for (j = 0; h[j]; j++) if (e && f.nodeName(h[j], "script") && (!h[j].type || h[j].type.toLowerCase() === "text/javascript")) e.push(h[j].parentNode ? h[j].parentNode.removeChild(h[j]) : h[j]);
                else {
                    if (h[j].nodeType === 1) {
                        var s = f.grep(h[j].getElementsByTagName("script"), g);
                        h.splice.apply(h, [j + 1, 0].concat(s))
                    }
                    d.appendChild(h[j])
                }
            }
            return h
        },
        cleanData: function (a) {
            var b, c, d = f.cache,
                e = f.expando,
                g = f.event.special,
                h = f.support.deleteExpando;
            for (var i = 0, j;
            (j = a[i]) != null; i++) {
                if (j.nodeName && f.noData[j.nodeName.toLowerCase()]) continue;
                c = j[f.expando];
                if (c) {
                    b = d[c] && d[c][e];
                    if (b && b.events) {
                        for (var k in b.events) g[k] ? f.event.remove(j, k) : f.removeEvent(j, k, b.handle);
                        b.handle && (b.handle.elem = null)
                    }
                    h ? delete j[f.expando] : j.removeAttribute && j.removeAttribute(f.expando), delete d[c]
                }
            }
        }
    });
    var bo = /alpha\([^)]*\)/i,
        bp = /opacity=([^)]*)/,
        bq = /-([a-z])/ig,
        br = /([A-Z]|^ms)/g,
        bs = /^-?\d+(?:px)?$/i,
        bt = /^-?\d/,
        bu = /^[+\-]=/,
        bv = /[^+\-\.\de]+/g,
        bw = {
            position: "absolute",
            visibility: "hidden",
            display: "block"
        },
        bx = ["Left", "Right"],
        by = ["Top", "Bottom"],
        bz, bA, bB, bC = function (a, b) {
            return b.toUpperCase()
        };
    f.fn.css = function (a, c) {
        if (arguments.length === 2 && c === b) return this;
        return f.access(this, a, c, !0, function (a, c, d) {
            return d !== b ? f.style(a, c, d) : f.css(a, c)
        })
    }, f.extend({
        cssHooks: {
            opacity: {
                get: function (a, b) {
                    if (b) {
                        var c = bz(a, "opacity", "opacity");
                        return c === "" ? "1" : c
                    }
                    return a.style.opacity
                }
            }
        },
        cssNumber: {
            zIndex: !0,
            fontWeight: !0,
            opacity: !0,
            zoom: !0,
            lineHeight: !0,
            widows: !0,
            orphans: !0
        },
        cssProps: {
            "float": f.support.cssFloat ? "cssFloat" : "styleFloat"
        },
        style: function (a, c, d, e) {
            if ( !! a && a.nodeType !== 3 && a.nodeType !== 8 && !! a.style) {
                var g, h, i = f.camelCase(c),
                    j = a.style,
                    k = f.cssHooks[i];
                c = f.cssProps[i] || i;
                if (d === b) {
                    if (k && "get" in k && (g = k.get(a, !1, e)) !== b) return g;
                    return j[c]
                }
                h = typeof d;
                if (h === "number" && isNaN(d) || d == null) return;
                h === "string" && bu.test(d) && (d = +d.replace(bv, "") + parseFloat(f.css(a, c))), h === "number" && !f.cssNumber[i] && (d += "px");
                if (!k || !("set" in k) || (d = k.set(a, d)) !== b) try {
                    j[c] = d
                } catch (l) {}
            }
        },
        css: function (a, c, d) {
            var e, g;
            c = f.camelCase(c), g = f.cssHooks[c], c = f.cssProps[c] || c, c === "cssFloat" && (c = "float");
            if (g && "get" in g && (e = g.get(a, !0, d)) !== b) return e;
            if (bz) return bz(a, c)
        },
        swap: function (a, b, c) {
            var d = {};
            for (var e in b) d[e] = a.style[e], a.style[e] = b[e];
            c.call(a);
            for (e in b) a.style[e] = d[e]
        },
        camelCase: function (a) {
            return a.replace(bq, bC)
        }
    }), f.curCSS = f.css, f.each(["height", "width"], function (a, b) {
        f.cssHooks[b] = {
            get: function (a, c, d) {
                var e;
                if (c) {
                    a.offsetWidth !== 0 ? e = bD(a, b, d) : f.swap(a, bw, function () {
                        e = bD(a, b, d)
                    });
                    if (e <= 0) {
                        e = bz(a, b, b), e === "0px" && bB && (e = bB(a, b, b));
                        if (e != null) return e === "" || e === "auto" ? "0px" : e
                    }
                    if (e < 0 || e == null) {
                        e = a.style[b];
                        return e === "" || e === "auto" ? "0px" : e
                    }
                    return typeof e == "string" ? e : e + "px"
                }
            },
            set: function (a, b) {
                if (!bs.test(b)) return b;
                b = parseFloat(b);
                if (b >= 0) return b + "px"
            }
        }
    }), f.support.opacity || (f.cssHooks.opacity = {
        get: function (a, b) {
            return bp.test((b && a.currentStyle ? a.currentStyle.filter : a.style.filter) || "") ? parseFloat(RegExp.$1) / 100 + "" : b ? "1" : ""
        },
        set: function (a, b) {
            var c = a.style,
                d = a.currentStyle;
            c.zoom = 1;
            var e = f.isNaN(b) ? "" : "alpha(opacity=" + b * 100 + ")",
                g = d && d.filter || c.filter || "";
            c.filter = bo.test(g) ? g.replace(bo, e) : g + " " + e
        }
    }), f(function () {
        f.support.reliableMarginRight || (f.cssHooks.marginRight = {
            get: function (a, b) {
                var c;
                f.swap(a, {
                    display: "inline-block"
                }, function () {
                    b ? c = bz(a, "margin-right", "marginRight") : c = a.style.marginRight
                });
                return c
            }
        })
    }), c.defaultView && c.defaultView.getComputedStyle && (bA = function (a, c) {
        var d, e, g;
        c = c.replace(br, "-$1").toLowerCase();
        if (!(e = a.ownerDocument.defaultView)) return b;
        if (g = e.getComputedStyle(a, null)) d = g.getPropertyValue(c), d === "" && !f.contains(a.ownerDocument.documentElement, a) && (d = f.style(a, c));
        return d
    }), c.documentElement.currentStyle && (bB = function (a, b) {
        var c, d = a.currentStyle && a.currentStyle[b],
            e = a.runtimeStyle && a.runtimeStyle[b],
            f = a.style;
        !bs.test(d) && bt.test(d) && (c = f.left, e && (a.runtimeStyle.left = a.currentStyle.left), f.left = b === "fontSize" ? "1em" : d || 0, d = f.pixelLeft + "px", f.left = c, e && (a.runtimeStyle.left = e));
        return d === "" ? "auto" : d
    }), bz = bA || bB, f.expr && f.expr.filters && (f.expr.filters.hidden = function (a) {
        var b = a.offsetWidth,
            c = a.offsetHeight;
        return b === 0 && c === 0 || !f.support.reliableHiddenOffsets && (a.style.display || f.css(a, "display")) === "none"
    }, f.expr.filters.visible = function (a) {
        return !f.expr.filters.hidden(a)
    });
    var bE = /%20/g,
        bF = /\[\]$/,
        bG = /\r?\n/g,
        bH = /#.*$/,
        bI = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg,
        bJ = /^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
        bK = /^(?:about|app|app\-storage|.+\-extension|file|widget):$/,
        bL = /^(?:GET|HEAD)$/,
        bM = /^\/\//,
        bN = /\?/,
        bO = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
        bP = /^(?:select|textarea)/i,
        bQ = /\s+/,
        bR = /([?&])_=[^&]*/,
        bS = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,
        bT = f.fn.load,
        bU = {},
        bV = {},
        bW, bX;
    try {
        bW = e.href
    } catch (bY) {
        bW = c.createElement("a"), bW.href = "", bW = bW.href
    }
    bX = bS.exec(bW.toLowerCase()) || [], f.fn.extend({
        load: function (a, c, d) {
            if (typeof a != "string" && bT) return bT.apply(this, arguments);
            if (!this.length) return this;
            var e = a.indexOf(" ");
            if (e >= 0) {
                var g = a.slice(e, a.length);
                a = a.slice(0, e)
            }
            var h = "GET";
            c && (f.isFunction(c) ? (d = c, c = b) : typeof c == "object" && (c = f.param(c, f.ajaxSettings.traditional), h = "POST"));
            var i = this;
            f.ajax({
                url: a,
                type: h,
                dataType: "html",
                data: c,
                complete: function (a, b, c) {
                    c = a.responseText, a.isResolved() && (a.done(function (a) {
                        c = a
                    }), i.html(g ? f("<div>").append(c.replace(bO, "")).find(g) : c)), d && i.each(d, [c, b, a])
                }
            });
            return this
        },
        serialize: function () {
            return f.param(this.serializeArray())
        },
        serializeArray: function () {
            return this.map(function () {
                return this.elements ? f.makeArray(this.elements) : this
            }).filter(function () {
                return this.name && !this.disabled && (this.checked || bP.test(this.nodeName) || bJ.test(this.type))
            }).map(function (a, b) {
                var c = f(this).val();
                return c == null ? null : f.isArray(c) ? f.map(c, function (a, c) {
                    return {
                        name: b.name,
                        value: a.replace(bG, "\r\n")
                    }
                }) : {
                    name: b.name,
                    value: c.replace(bG, "\r\n")
                }
            }).get()
        }
    }), f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), function (a, b) {
        f.fn[b] = function (a) {
            return this.bind(b, a)
        }
    }), f.each(["get", "post"], function (a, c) {
        f[c] = function (a, d, e, g) {
            f.isFunction(d) && (g = g || e, e = d, d = b);
            return f.ajax({
                type: c,
                url: a,
                data: d,
                success: e,
                dataType: g
            })
        }
    }), f.extend({
        getScript: function (a, c) {
            return f.get(a, b, c, "script")
        },
        getJSON: function (a, b, c) {
            return f.get(a, b, c, "json")
        },
        ajaxSetup: function (a, b) {
            b ? f.extend(!0, a, f.ajaxSettings, b) : (b = a, a = f.extend(!0, f.ajaxSettings, b));
            for (var c in {
                context: 1,
                url: 1
            }) c in b ? a[c] = b[c] : c in f.ajaxSettings && (a[c] = f.ajaxSettings[c]);
            return a
        },
        ajaxSettings: {
            url: bW,
            isLocal: bK.test(bX[1]),
            global: !0,
            type: "GET",
            contentType: "application/x-www-form-urlencoded",
            processData: !0,
            async: !0,
            accepts: {
                xml: "application/xml, text/xml",
                html: "text/html",
                text: "text/plain",
                json: "application/json, text/javascript",
                "*": "*/*"
            },
            contents: {
                xml: /xml/,
                html: /html/,
                json: /json/
            },
            responseFields: {
                xml: "responseXML",
                text: "responseText"
            },
            converters: {
                "* text": a.String,
                "text html": !0,
                "text json": f.parseJSON,
                "text xml": f.parseXML
            }
        },
        ajaxPrefilter: bZ(bU),
        ajaxTransport: bZ(bV),
        ajax: function (a, c) {
            function w(a, c, l, m) {
                if (s !== 2) {
                    s = 2, q && clearTimeout(q), p = b, n = m || "", v.readyState = a ? 4 : 0;
                    var o, r, u, w = l ? ca(d, v, l) : b,
                        x, y;
                    if (a >= 200 && a < 300 || a === 304) {
                        if (d.ifModified) {
                            if (x = v.getResponseHeader("Last-Modified")) f.lastModified[k] = x;
                            if (y = v.getResponseHeader("Etag")) f.etag[k] = y
                        }
                        if (a === 304) c = "notmodified", o = !0;
                        else try {
                            r = cb(d, w), c = "success", o = !0
                        } catch (z) {
                            c = "parsererror", u = z
                        }
                    } else {
                        u = c;
                        if (!c || a) c = "error", a < 0 && (a = 0)
                    }
                    v.status = a, v.statusText = c, o ? h.resolveWith(e, [r, c, v]) : h.rejectWith(e, [v, c, u]), v.statusCode(j), j = b, t && g.trigger("ajax" + (o ? "Success" : "Error"), [v, d, o ? r : u]), i.resolveWith(e, [v, c]), t && (g.trigger("ajaxComplete", [v, d]), --f.active || f.event.trigger("ajaxStop"))
                }
            }
            typeof a == "object" && (c = a, a = b), c = c || {};
            var d = f.ajaxSetup({}, c),
                e = d.context || d,
                g = e !== d && (e.nodeType || e instanceof f) ? f(e) : f.event,
                h = f.Deferred(),
                i = f._Deferred(),
                j = d.statusCode || {},
                k, l = {},
                m = {},
                n, o, p, q, r, s = 0,
                t, u, v = {
                    readyState: 0,
                    setRequestHeader: function (a, b) {
                        if (!s) {
                            var c = a.toLowerCase();
                            a = m[c] = m[c] || a, l[a] = b
                        }
                        return this
                    },
                    getAllResponseHeaders: function () {
                        return s === 2 ? n : null
                    },
                    getResponseHeader: function (a) {
                        var c;
                        if (s === 2) {
                            if (!o) {
                                o = {};
                                while (c = bI.exec(n)) o[c[1].toLowerCase()] = c[2]
                            }
                            c = o[a.toLowerCase()]
                        }
                        return c === b ? null : c
                    },
                    overrideMimeType: function (a) {
                        s || (d.mimeType = a);
                        return this
                    },
                    abort: function (a) {
                        a = a || "abort", p && p.abort(a), w(0, a);
                        return this
                    }
                };
            h.promise(v), v.success = v.done, v.error = v.fail, v.complete = i.done, v.statusCode = function (a) {
                if (a) {
                    var b;
                    if (s < 2) for (b in a) j[b] = [j[b], a[b]];
                    else b = a[v.status], v.then(b, b)
                }
                return this
            }, d.url = ((a || d.url) + "").replace(bH, "").replace(bM, bX[1] + "//"), d.dataTypes = f.trim(d.dataType || "*").toLowerCase().split(bQ), d.crossDomain == null && (r = bS.exec(d.url.toLowerCase()), d.crossDomain = !(!r || r[1] == bX[1] && r[2] == bX[2] && (r[3] || (r[1] === "http:" ? 80 : 443)) == (bX[3] || (bX[1] === "http:" ? 80 : 443)))), d.data && d.processData && typeof d.data != "string" && (d.data = f.param(d.data, d.traditional)), b$(bU, d, c, v);
            if (s === 2) return !1;
            t = d.global, d.type = d.type.toUpperCase(), d.hasContent = !bL.test(d.type), t && f.active++ === 0 && f.event.trigger("ajaxStart");
            if (!d.hasContent) {
                d.data && (d.url += (bN.test(d.url) ? "&" : "?") + d.data), k = d.url;
                if (d.cache === !1) {
                    var x = f.now(),
                        y = d.url.replace(bR, "$1_=" + x);
                    d.url = y + (y === d.url ? (bN.test(d.url) ? "&" : "?") + "_=" + x : "")
                }
            }(d.data && d.hasContent && d.contentType !== !1 || c.contentType) && v.setRequestHeader("Content-Type", d.contentType), d.ifModified && (k = k || d.url, f.lastModified[k] && v.setRequestHeader("If-Modified-Since", f.lastModified[k]), f.etag[k] && v.setRequestHeader("If-None-Match", f.etag[k])), v.setRequestHeader("Accept", d.dataTypes[0] && d.accepts[d.dataTypes[0]] ? d.accepts[d.dataTypes[0]] + (d.dataTypes[0] !== "*" ? ", */*; q=0.01" : "") : d.accepts["*"]);
            for (u in d.headers) v.setRequestHeader(u, d.headers[u]);
            if (d.beforeSend && (d.beforeSend.call(e, v, d) === !1 || s === 2)) {
                v.abort();
                return !1
            }
            for (u in {
                success: 1,
                error: 1,
                complete: 1
            }) v[u](d[u]);
            p = b$(bV, d, c, v);
            if (!p) w(-1, "No Transport");
            else {
                v.readyState = 1, t && g.trigger("ajaxSend", [v, d]), d.async && d.timeout > 0 && (q = setTimeout(function () {
                    v.abort("timeout")
                }, d.timeout));
                try {
                    s = 1, p.send(l, w)
                } catch (z) {
                    status < 2 ? w(-1, z) : f.error(z)
                }
            }
            return v
        },
        param: function (a, c) {
            var d = [],
                e = function (a, b) {
                    b = f.isFunction(b) ? b() : b, d[d.length] = encodeURIComponent(a) + "=" + encodeURIComponent(b)
                };
            c === b && (c = f.ajaxSettings.traditional);
            if (f.isArray(a) || a.jquery && !f.isPlainObject(a)) f.each(a, function () {
                e(this.name, this.value)
            });
            else for (var g in a) b_(g, a[g], c, e);
            return d.join("&").replace(bE, "+")
        }
    }), f.extend({
        active: 0,
        lastModified: {},
        etag: {}
    });
    var cc = f.now(),
        cd = /(\=)\?(&|$)|\?\?/i;
    f.ajaxSetup({
        jsonp: "callback",
        jsonpCallback: function () {
            return f.expando + "_" + cc++
        }
    }), f.ajaxPrefilter("json jsonp", function (b, c, d) {
        var e = b.contentType === "application/x-www-form-urlencoded" && typeof b.data == "string";
        if (b.dataTypes[0] === "jsonp" || b.jsonp !== !1 && (cd.test(b.url) || e && cd.test(b.data))) {
            var g, h = b.jsonpCallback = f.isFunction(b.jsonpCallback) ? b.jsonpCallback() : b.jsonpCallback,
                i = a[h],
                j = b.url,
                k = b.data,
                l = "$1" + h + "$2";
            b.jsonp !== !1 && (j = j.replace(cd, l), b.url === j && (e && (k = k.replace(cd, l)), b.data === k && (j += (/\?/.test(j) ? "&" : "?") + b.jsonp + "=" + h))), b.url = j, b.data = k, a[h] = function (a) {
                g = [a]
            }, d.always(function () {
                a[h] = i, g && f.isFunction(i) && a[h](g[0])
            }), b.converters["script json"] = function () {
                g || f.error(h + " was not called");
                return g[0]
            }, b.dataTypes[0] = "json";
            return "script"
        }
    }), f.ajaxSetup({
        accepts: {
            script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
        },
        contents: {
            script: /javascript|ecmascript/
        },
        converters: {
            "text script": function (a) {
                f.globalEval(a);
                return a
            }
        }
    }), f.ajaxPrefilter("script", function (a) {
        a.cache === b && (a.cache = !1), a.crossDomain && (a.type = "GET", a.global = !1)
    }), f.ajaxTransport("script", function (a) {
        if (a.crossDomain) {
            var d, e = c.head || c.getElementsByTagName("head")[0] || c.documentElement;
            return {
                send: function (f, g) {
                    d = c.createElement("script"), d.async = "async", a.scriptCharset && (d.charset = a.scriptCharset), d.src = a.url, d.onload = d.onreadystatechange = function (a, c) {
                        if (c || !d.readyState || /loaded|complete/.test(d.readyState)) d.onload = d.onreadystatechange = null, e && d.parentNode && e.removeChild(d), d = b, c || g(200, "success")
                    }, e.insertBefore(d, e.firstChild)
                },
                abort: function () {
                    d && d.onload(0, 1)
                }
            }
        }
    });
    var ce = a.ActiveXObject ?
    function () {
        for (var a in cg) cg[a](0, 1)
    } : !1, cf = 0, cg;
    f.ajaxSettings.xhr = a.ActiveXObject ?
    function () {
        return !this.isLocal && ch() || ci()
    } : ch, function (a) {
        f.extend(f.support, {
            ajax: !! a,
            cors: !! a && "withCredentials" in a
        })
    }(f.ajaxSettings.xhr()), f.support.ajax && f.ajaxTransport(function (c) {
        if (!c.crossDomain || f.support.cors) {
            var d;
            return {
                send: function (e, g) {
                    var h = c.xhr(),
                        i, j;
                    c.username ? h.open(c.type, c.url, c.async, c.username, c.password) : h.open(c.type, c.url, c.async);
                    if (c.xhrFields) for (j in c.xhrFields) h[j] = c.xhrFields[j];
                    c.mimeType && h.overrideMimeType && h.overrideMimeType(c.mimeType), !c.crossDomain && !e["X-Requested-With"] && (e["X-Requested-With"] = "XMLHttpRequest");
                    try {
                        for (j in e) h.setRequestHeader(j, e[j])
                    } catch (k) {}
                    h.send(c.hasContent && c.data || null), d = function (a, e) {
                        var j, k, l, m, n;
                        try {
                            if (d && (e || h.readyState === 4)) {
                                d = b, i && (h.onreadystatechange = f.noop, ce && delete cg[i]);
                                if (e) h.readyState !== 4 && h.abort();
                                else {
                                    j = h.status, l = h.getAllResponseHeaders(), m = {}, n = h.responseXML, n && n.documentElement && (m.xml = n), m.text = h.responseText;
                                    try {
                                        k = h.statusText
                                    } catch (o) {
                                        k = ""
                                    }!j && c.isLocal && !c.crossDomain ? j = m.text ? 200 : 404 : j === 1223 && (j = 204)
                                }
                            }
                        } catch (p) {
                            e || g(-1, p)
                        }
                        m && g(j, k, m, l)
                    }, !c.async || h.readyState === 4 ? d() : (i = ++cf, ce && (cg || (cg = {}, f(a).unload(ce)), cg[i] = d), h.onreadystatechange = d)
                },
                abort: function () {
                    d && d(0, 1)
                }
            }
        }
    });
    var cj = {},
        ck, cl, cm = /^(?:toggle|show|hide)$/,
        cn = /^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,
        co, cp = [
            ["height", "marginTop", "marginBottom", "paddingTop", "paddingBottom"],
            ["width", "marginLeft", "marginRight", "paddingLeft", "paddingRight"],
            ["opacity"]
        ],
        cq, cr = a.webkitRequestAnimationFrame || a.mozRequestAnimationFrame || a.oRequestAnimationFrame;
    f.fn.extend({
        show: function (a, b, c) {
            var d, e;
            if (a || a === 0) return this.animate(cu("show", 3), a, b, c);
            for (var g = 0, h = this.length; g < h; g++) d = this[g], d.style && (e = d.style.display, !f._data(d, "olddisplay") && e === "none" && (e = d.style.display = ""), e === "" && f.css(d, "display") === "none" && f._data(d, "olddisplay", cv(d.nodeName)));
            for (g = 0; g < h; g++) {
                d = this[g];
                if (d.style) {
                    e = d.style.display;
                    if (e === "" || e === "none") d.style.display = f._data(d, "olddisplay") || ""
                }
            }
            return this
        },
        hide: function (a, b, c) {
            if (a || a === 0) return this.animate(cu("hide", 3), a, b, c);
            for (var d = 0, e = this.length; d < e; d++) if (this[d].style) {
                var g = f.css(this[d], "display");
                g !== "none" && !f._data(this[d], "olddisplay") && f._data(this[d], "olddisplay", g)
            }
            for (d = 0; d < e; d++) this[d].style && (this[d].style.display = "none");
            return this
        },
        _toggle: f.fn.toggle,
        toggle: function (a, b, c) {
            var d = typeof a == "boolean";
            f.isFunction(a) && f.isFunction(b) ? this._toggle.apply(this, arguments) : a == null || d ? this.each(function () {
                var b = d ? a : f(this).is(":hidden");
                f(this)[b ? "show" : "hide"]()
            }) : this.animate(cu("toggle", 3), a, b, c);
            return this
        },
        fadeTo: function (a, b, c, d) {
            return this.filter(":hidden").css("opacity", 0).show().end().animate({
                opacity: b
            }, a, c, d)
        },
        animate: function (a, b, c, d) {
            var e = f.speed(b, c, d);
            if (f.isEmptyObject(a)) return this.each(e.complete, [!1]);
            a = f.extend({}, a);
            return this[e.queue === !1 ? "each" : "queue"](function () {
                e.queue === !1 && f._mark(this);
                var b = f.extend({}, e),
                    c = this.nodeType === 1,
                    d = c && f(this).is(":hidden"),
                    g, h, i, j, k, l, m, n, o;
                b.animatedProperties = {};
                for (i in a) {
                    g = f.camelCase(i), i !== g && (a[g] = a[i], delete a[i]), h = a[g], f.isArray(h) ? (b.animatedProperties[g] = h[1], h = a[g] = h[0]) : b.animatedProperties[g] = b.specialEasing && b.specialEasing[g] || b.easing || "swing";
                    if (h === "hide" && d || h === "show" && !d) return b.complete.call(this);
                    c && (g === "height" || g === "width") && (b.overflow = [this.style.overflow, this.style.overflowX, this.style.overflowY], f.css(this, "display") === "inline" && f.css(this, "float") === "none" && (f.support.inlineBlockNeedsLayout ? (j = cv(this.nodeName), j === "inline" ? this.style.display = "inline-block" : (this.style.display = "inline", this.style.zoom = 1)) : this.style.display = "inline-block"))
                }
                b.overflow != null && (this.style.overflow = "hidden");
                for (i in a) k = new f.fx(this, b, i), h = a[i], cm.test(h) ? k[h === "toggle" ? d ? "show" : "hide" : h]() : (l = cn.exec(h), m = k.cur(), l ? (n = parseFloat(l[2]), o = l[3] || (f.cssNumber[i] ? "" : "px"), o !== "px" && (f.style(this, i, (n || 1) + o), m = (n || 1) / k.cur() * m, f.style(this, i, m + o)), l[1] && (n = (l[1] === "-=" ? -1 : 1) * n + m), k.custom(m, n, o)) : k.custom(m, h, ""));
                return !0
            })
        },
        stop: function (a, b) {
            a && this.queue([]), this.each(function () {
                var a = f.timers,
                    c = a.length;
                b || f._unmark(!0, this);
                while (c--) a[c].elem === this && (b && a[c](!0), a.splice(c, 1))
            }), b || this.dequeue();
            return this
        }
    }), f.each({
        slideDown: cu("show", 1),
        slideUp: cu("hide", 1),
        slideToggle: cu("toggle", 1),
        fadeIn: {
            opacity: "show"
        },
        fadeOut: {
            opacity: "hide"
        },
        fadeToggle: {
            opacity: "toggle"
        }
    }, function (a, b) {
        f.fn[a] = function (a, c, d) {
            return this.animate(b, a, c, d)
        }
    }), f.extend({
        speed: function (a, b, c) {
            var d = a && typeof a == "object" ? f.extend({}, a) : {
                complete: c || !c && b || f.isFunction(a) && a,
                duration: a,
                easing: c && b || b && !f.isFunction(b) && b
            };
            d.duration = f.fx.off ? 0 : typeof d.duration == "number" ? d.duration : d.duration in f.fx.speeds ? f.fx.speeds[d.duration] : f.fx.speeds._default, d.old = d.complete, d.complete = function (a) {
                d.queue !== !1 ? f.dequeue(this) : a !== !1 && f._unmark(this), f.isFunction(d.old) && d.old.call(this)
            };
            return d
        },
        easing: {
            linear: function (a, b, c, d) {
                return c + d * a
            },
            swing: function (a, b, c, d) {
                return (-Math.cos(a * Math.PI) / 2 + .5) * d + c
            }
        },
        timers: [],
        fx: function (a, b, c) {
            this.options = b, this.elem = a, this.prop = c, b.orig = b.orig || {}
        }
    }), f.fx.prototype = {
        update: function () {
            this.options.step && this.options.step.call(this.elem, this.now, this), (f.fx.step[this.prop] || f.fx.step._default)(this)
        },
        cur: function () {
            if (this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null)) return this.elem[this.prop];
            var a, b = f.css(this.elem, this.prop);
            return isNaN(a = parseFloat(b)) ? !b || b === "auto" ? 0 : b : a
        },
        custom: function (a, b, c) {
            function h(a) {
                return d.step(a)
            }
            var d = this,
                e = f.fx,
                g;
            this.startTime = cq || cs(), this.start = a, this.end = b, this.unit = c || this.unit || (f.cssNumber[this.prop] ? "" : "px"), this.now = this.start, this.pos = this.state = 0, h.elem = this.elem, h() && f.timers.push(h) && !co && (cr ? (co = 1, g = function () {
                co && (cr(g), e.tick())
            }, cr(g)) : co = setInterval(e.tick, e.interval))
        },
        show: function () {
            this.options.orig[this.prop] = f.style(this.elem, this.prop), this.options.show = !0, this.custom(this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur()), f(this.elem).show()
        },
        hide: function () {
            this.options.orig[this.prop] = f.style(this.elem, this.prop), this.options.hide = !0, this.custom(this.cur(), 0)
        },
        step: function (a) {
            var b = cq || cs(),
                c = !0,
                d = this.elem,
                e = this.options,
                g, h;
            if (a || b >= e.duration + this.startTime) {
                this.now = this.end, this.pos = this.state = 1, this.update(), e.animatedProperties[this.prop] = !0;
                for (g in e.animatedProperties) e.animatedProperties[g] !== !0 && (c = !1);
                if (c) {
                    e.overflow != null && !f.support.shrinkWrapBlocks && f.each(["", "X", "Y"], function (a, b) {
                        d.style["overflow" + b] = e.overflow[a]
                    }), e.hide && f(d).hide();
                    if (e.hide || e.show) for (var i in e.animatedProperties) f.style(d, i, e.orig[i]);
                    e.complete.call(d)
                }
                return !1
            }
            e.duration == Infinity ? this.now = b : (h = b - this.startTime, this.state = h / e.duration, this.pos = f.easing[e.animatedProperties[this.prop]](this.state, h, 0, 1, e.duration), this.now = this.start + (this.end - this.start) * this.pos), this.update();
            return !0
        }
    }, f.extend(f.fx, {
        tick: function () {
            for (var a = f.timers, b = 0; b < a.length; ++b) a[b]() || a.splice(b--, 1);
            a.length || f.fx.stop()
        },
        interval: 13,
        stop: function () {
            clearInterval(co), co = null
        },
        speeds: {
            slow: 600,
            fast: 200,
            _default: 400
        },
        step: {
            opacity: function (a) {
                f.style(a.elem, "opacity", a.now)
            },
            _default: function (a) {
                a.elem.style && a.elem.style[a.prop] != null ? a.elem.style[a.prop] = (a.prop === "width" || a.prop === "height" ? Math.max(0, a.now) : a.now) + a.unit : a.elem[a.prop] = a.now
            }
        }
    }), f.expr && f.expr.filters && (f.expr.filters.animated = function (a) {
        return f.grep(f.timers, function (b) {
            return a === b.elem
        }).length
    });
    var cw = /^t(?:able|d|h)$/i,
        cx = /^(?:body|html)$/i;
    "getBoundingClientRect" in c.documentElement ? f.fn.offset = function (a) {
        var b = this[0],
            c;
        if (a) return this.each(function (b) {
            f.offset.setOffset(this, a, b)
        });
        if (!b || !b.ownerDocument) return null;
        if (b === b.ownerDocument.body) return f.offset.bodyOffset(b);
        try {
            c = b.getBoundingClientRect()
        } catch (d) {}
        var e = b.ownerDocument,
            g = e.documentElement;
        if (!c || !f.contains(g, b)) return c ? {
            top: c.top,
            left: c.left
        } : {
            top: 0,
            left: 0
        };
        var h = e.body,
            i = cy(e),
            j = g.clientTop || h.clientTop || 0,
            k = g.clientLeft || h.clientLeft || 0,
            l = i.pageYOffset || f.support.boxModel && g.scrollTop || h.scrollTop,
            m = i.pageXOffset || f.support.boxModel && g.scrollLeft || h.scrollLeft,
            n = c.top + l - j,
            o = c.left + m - k;
        return {
            top: n,
            left: o
        }
    } : f.fn.offset = function (a) {
        var b = this[0];
        if (a) return this.each(function (b) {
            f.offset.setOffset(this, a, b)
        });
        if (!b || !b.ownerDocument) return null;
        if (b === b.ownerDocument.body) return f.offset.bodyOffset(b);
        f.offset.initialize();
        var c, d = b.offsetParent,
            e = b,
            g = b.ownerDocument,
            h = g.documentElement,
            i = g.body,
            j = g.defaultView,
            k = j ? j.getComputedStyle(b, null) : b.currentStyle,
            l = b.offsetTop,
            m = b.offsetLeft;
        while ((b = b.parentNode) && b !== i && b !== h) {
            if (f.offset.supportsFixedPosition && k.position === "fixed") break;
            c = j ? j.getComputedStyle(b, null) : b.currentStyle, l -= b.scrollTop, m -= b.scrollLeft, b === d && (l += b.offsetTop, m += b.offsetLeft, f.offset.doesNotAddBorder && (!f.offset.doesAddBorderForTableAndCells || !cw.test(b.nodeName)) && (l += parseFloat(c.borderTopWidth) || 0, m += parseFloat(c.borderLeftWidth) || 0), e = d, d = b.offsetParent), f.offset.subtractsBorderForOverflowNotVisible && c.overflow !== "visible" && (l += parseFloat(c.borderTopWidth) || 0, m += parseFloat(c.borderLeftWidth) || 0), k = c
        }
        if (k.position === "relative" || k.position === "static") l += i.offsetTop, m += i.offsetLeft;
        f.offset.supportsFixedPosition && k.position === "fixed" && (l += Math.max(h.scrollTop, i.scrollTop), m += Math.max(h.scrollLeft, i.scrollLeft));
        return {
            top: l,
            left: m
        }
    }, f.offset = {
        initialize: function () {
            var a = c.body,
                b = c.createElement("div"),
                d, e, g, h, i = parseFloat(f.css(a, "marginTop")) || 0,
                j = "<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
            f.extend(b.style, {
                position: "absolute",
                top: 0,
                left: 0,
                margin: 0,
                border: 0,
                width: "1px",
                height: "1px",
                visibility: "hidden"
            }), b.innerHTML = j, a.insertBefore(b, a.firstChild), d = b.firstChild, e = d.firstChild, h = d.nextSibling.firstChild.firstChild, this.doesNotAddBorder = e.offsetTop !== 5, this.doesAddBorderForTableAndCells = h.offsetTop === 5, e.style.position = "fixed", e.style.top = "20px", this.supportsFixedPosition = e.offsetTop === 20 || e.offsetTop === 15, e.style.position = e.style.top = "", d.style.overflow = "hidden", d.style.position = "relative", this.subtractsBorderForOverflowNotVisible = e.offsetTop === -5, this.doesNotIncludeMarginInBodyOffset = a.offsetTop !== i, a.removeChild(b), f.offset.initialize = f.noop
        },
        bodyOffset: function (a) {
            var b = a.offsetTop,
                c = a.offsetLeft;
            f.offset.initialize(), f.offset.doesNotIncludeMarginInBodyOffset && (b += parseFloat(f.css(a, "marginTop")) || 0, c += parseFloat(f.css(a, "marginLeft")) || 0);
            return {
                top: b,
                left: c
            }
        },
        setOffset: function (a, b, c) {
            var d = f.css(a, "position");
            d === "static" && (a.style.position = "relative");
            var e = f(a),
                g = e.offset(),
                h = f.css(a, "top"),
                i = f.css(a, "left"),
                j = (d === "absolute" || d === "fixed") && f.inArray("auto", [h, i]) > -1,
                k = {},
                l = {},
                m, n;
            j ? (l = e.position(), m = l.top, n = l.left) : (m = parseFloat(h) || 0, n = parseFloat(i) || 0), f.isFunction(b) && (b = b.call(a, c, g)), b.top != null && (k.top = b.top - g.top + m), b.left != null && (k.left = b.left - g.left + n), "using" in b ? b.using.call(a, k) : e.css(k)
        }
    }, f.fn.extend({
        position: function () {
            if (!this[0]) return null;
            var a = this[0],
                b = this.offsetParent(),
                c = this.offset(),
                d = cx.test(b[0].nodeName) ? {
                    top: 0,
                    left: 0
                } : b.offset();
            c.top -= parseFloat(f.css(a, "marginTop")) || 0, c.left -= parseFloat(f.css(a, "marginLeft")) || 0, d.top += parseFloat(f.css(b[0], "borderTopWidth")) || 0, d.left += parseFloat(f.css(b[0], "borderLeftWidth")) || 0;
            return {
                top: c.top - d.top,
                left: c.left - d.left
            }
        },
        offsetParent: function () {
            return this.map(function () {
                var a = this.offsetParent || c.body;
                while (a && !cx.test(a.nodeName) && f.css(a, "position") === "static") a = a.offsetParent;
                return a
            })
        }
    }), f.each(["Left", "Top"], function (a, c) {
        var d = "scroll" + c;
        f.fn[d] = function (c) {
            var e, g;
            if (c === b) {
                e = this[0];
                if (!e) return null;
                g = cy(e);
                return g ? "pageXOffset" in g ? g[a ? "pageYOffset" : "pageXOffset"] : f.support.boxModel && g.document.documentElement[d] || g.document.body[d] : e[d]
            }
            return this.each(function () {
                g = cy(this), g ? g.scrollTo(a ? f(g).scrollLeft() : c, a ? c : f(g).scrollTop()) : this[d] = c
            })
        }
    }), f.each(["Height", "Width"], function (a, c) {
        var d = c.toLowerCase();
        f.fn["inner" + c] = function () {
            return this[0] ? parseFloat(f.css(this[0], d, "padding")) : null
        }, f.fn["outer" + c] = function (a) {
            return this[0] ? parseFloat(f.css(this[0], d, a ? "margin" : "border")) : null
        }, f.fn[d] = function (a) {
            var e = this[0];
            if (!e) return a == null ? null : this;
            if (f.isFunction(a)) return this.each(function (b) {
                var c = f(this);
                c[d](a.call(this, b, c[d]()))
            });
            if (f.isWindow(e)) {
                var g = e.document.documentElement["client" + c];
                return e.document.compatMode === "CSS1Compat" && g || e.document.body["client" + c] || g
            }
            if (e.nodeType === 9) return Math.max(e.documentElement["client" + c], e.body["scroll" + c], e.documentElement["scroll" + c], e.body["offset" + c], e.documentElement["offset" + c]);
            if (a === b) {
                var h = f.css(e, d),
                    i = parseFloat(h);
                return f.isNaN(i) ? h : i
            }
            return this.css(d, typeof a == "string" ? a : a + "px")
        }
    }), a.jQuery = a.$ = f
})(window);;
(function ($) {
    $.fn.extend({
        oldautocomplete: function (urlOrData, options) {
            var isUrl = typeof urlOrData == "string";
            options = $.extend({}, $.Autocompleter.defaults, {
                url: isUrl ? urlOrData : null,
                data: isUrl ? null : urlOrData,
                delay: isUrl ? $.Autocompleter.defaults.delay : 10,
                max: options && !options.scroll ? 10 : 150
            }, options);
            options.highlight = options.highlight ||
            function (value) {
                return value;
            };
            options.formatMatch = options.formatMatch || options.formatItem;
            return this.each(function () {
                new $.Autocompleter(this, options);
            });
        },
        result: function (handler) {
            return this.bind("result", handler);
        },
        search: function (handler) {
            return this.trigger("search", [handler]);
        },
        flushCache: function () {
            return this.trigger("flushCache");
        },
        setOptions: function (options) {
            return this.trigger("setOptions", [options]);
        },
        unautocomplete: function () {
            return this.trigger("unautocomplete");
        }
    });
    $.Autocompleter = function (input, options) {
        var KEY = {
            UP: 38,
            DOWN: 40,
            DEL: 46,
            TAB: 9,
            RETURN: 13,
            ESC: 27,
            COMMA: 188,
            PAGEUP: 33,
            PAGEDOWN: 34,
            BACKSPACE: 8
        };
        var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass);
        var timeout;
        var previousValue = "";
        var cache = $.Autocompleter.Cache(options);
        var hasFocus = 0;
        var lastKeyPressCode;
        var config = {
            mouseDownOnSelect: false
        };
        var select = $.Autocompleter.Select(options, input, selectCurrent, config);
        var blockSubmit;
        $.browser.opera && $(input.form).bind("submit.autocomplete", function () {
            if (blockSubmit) {
                blockSubmit = false;
                return false;
            }
        });
        $input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function (event) {
            hasFocus = 1;
            lastKeyPressCode = event.keyCode;
            switch (event.keyCode) {
            case KEY.UP:
                event.preventDefault();
                if (select.visible()) {
                    select.prev();
                } else {
                    onChange(0, true);
                }
                break;
            case KEY.DOWN:
                event.preventDefault();
                if (select.visible()) {
                    select.next();
                } else {
                    onChange(0, true);
                }
                break;
            case KEY.PAGEUP:
                event.preventDefault();
                if (select.visible()) {
                    select.pageUp();
                } else {
                    onChange(0, true);
                }
                break;
            case KEY.PAGEDOWN:
                event.preventDefault();
                if (select.visible()) {
                    select.pageDown();
                } else {
                    onChange(0, true);
                }
                break;
            case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
            case KEY.TAB:
            case KEY.RETURN:
                if (selectCurrent()) {
                    event.preventDefault();
                    blockSubmit = true;
                    return false;
                }
                break;
            case KEY.ESC:
                select.hide();
                break;
            default:
                clearTimeout(timeout);
                timeout = setTimeout(onChange, options.delay);
                break;
            }
        }).focus(function () {
            hasFocus++;
        }).blur(function () {
            hasFocus = 0;
            if (!config.mouseDownOnSelect) {
                hideResults();
            }
        }).click(function () {
            if (hasFocus++ > 1 && !select.visible()) {
                onChange(0, true);
            }
        }).bind("search", function (arg1, arg2) {
            var fn = arg2 ? arg2 : null;

            function findValueCallback(q, data) {
                var result, i;
                if (data && data.length) {
                    for (i = 0; i < data.length; i++) {
                        if (data[i].result.toLowerCase() == q.toLowerCase()) {
                            result = data[i];
                            break;
                        }
                    }
                }
                if (typeof fn == "function") {
                    fn(result);
                } else $input.trigger("result", result && [result.data, result.value]);
            }
            $.each(trimWords($input.val()), function (i, value) {
                request(value, findValueCallback, findValueCallback);
            });
        }).bind("flushCache", function () {
            cache.flush();
        }).bind("setOptions", function () {
            $.extend(options, arguments[1]);
            if ("data" in arguments[1]) cache.populate();
        }).bind("unautocomplete", function () {
            select.unbind();
            $input.unbind();
            $(input.form).unbind(".autocomplete");
        });

        function selectCurrent() {
            var selected = select.selected();
            if (!selected) {
                return false;
            }
            var v = selected.result;
            previousValue = v;
            if (options.multiple) {
                var words = trimWords($input.val());
                if (words.length > 1) {
                    words[getCursorEndPos(words)[1]] = v;
                    v = words.join(options.multipleSeparator);
                }
                v += options.multipleSeparator;
            }
            $input.val(v);
            hideResultsNow();
            $input.trigger("result", [selected.data, selected.value]);
            if ($.browser.msie && $input[0]) {
                textSelect($input[0], $input.val().length);
            }
            return true;
        }

        function textSelect(input, start, end) {
            var range;
            end = end || start;
            if (input.createTextRange) {
                range = input.createTextRange();
                range.collapse(true);
                range.moveEnd('character', end);
                range.moveStart('character', start);
                range.select();
            } else if (input.setSelectionRange) {
                input.focus();
                input.setSelectionRange(start, end);
            }
        }

        function getCursorEndPos(words) {
            var seperator = options.multipleSeparator.length,
                cursorAt = $(input).selection().start,
                wordAt, progress = 0;
            $.each(words, function (i, word) {
                progress += word.length;
                if (cursorAt <= progress) {
                    wordAt = i;
                    return false;
                }
                progress += seperator;
            });
            return [progress, wordAt];
        }

        function onChange(crap, skipPrevCheck) {
            if (lastKeyPressCode == KEY.DEL) {
                select.hide();
                return;
            }
            var currentValue = $input.val();
            if (!skipPrevCheck && currentValue == previousValue) {
                return;
            }
            previousValue = currentValue;
            currentValue = lastWord(currentValue);
            if (currentValue.length >= options.minChars) {
                $input.addClass(options.loadingClass);
                if (!options.matchCase) {
                    currentValue = currentValue.toLowerCase();
                }
                request(currentValue, receiveData, hideResultsNow);
            } else {
                stopLoading();
                select.hide();
            }
        };

        function trimWords(value) {
            if (!value) return [""];
            if (!options.multiple) return [$.trim(value)];
            return $.map(value.split(options.multipleSeparator), function (word) {
                return $.trim(value).length ? $.trim(word) : null;
            });
        }

        function lastWord(value) {
            if (!options.multiple) return value;
            var words = trimWords(value);
            if (words.length == 1) return words[0];
            var cursorAt = $(input).selection().start;
            if (cursorAt == value.length) {
                words = trimWords(value)
            } else {
                words = trimWords(value.replace(value.substring(cursorAt), ""));
            }
            return words[words.length - 1];
        }

        function autoFill(q, sValue) {
            if (options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE) {
                $input.val($input.val() + sValue.substring(lastWord(previousValue).length));
                $(input).selection(previousValue.length, previousValue.length + sValue.length);
            }
        };

        function hideResults() {
            clearTimeout(timeout);
            timeout = setTimeout(hideResultsNow, 200);
        };

        function hideResultsNow() {
            var wasVisible = select.visible();
            select.hide();
            clearTimeout(timeout);
            stopLoading();
            if (options.mustMatch) {
                $input.search(function (result) {
                    if (!result) {
                        if (options.multiple) {
                            var words = trimWords($input.val()).slice(0, -1);
                            $input.val(words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : ""));
                        } else {
                            $input.val("");
                            $input.trigger("result", null);
                        }
                    }
                });
            }
        };

        function receiveData(q, data) {
            if (data && data.length && hasFocus) {
                stopLoading();
                select.display(data, q);
                autoFill(q, data[0].value);
                select.show();
            } else {
                hideResultsNow();
            }
        };

        function request(term, success, failure) {
            if (!options.matchCase) term = term.toLowerCase();
            var data = cache.load(term);
            if (data && data.length) {
                success(term, data);
            } else if ((typeof options.url == "string") && (options.url.length > 0)) {
                var extraParams = {
                    timestamp: +new Date()
                };
                $.each(options.extraParams, function (key, param) {
                    extraParams[key] = typeof param == "function" ? param() : param;
                });
                $.ajax({
                    mode: "abort",
                    port: "autocomplete" + input.name,
                    dataType: options.dataType,
                    url: options.url,
                    data: $.extend({
                        q: lastWord(term),
                        limit: options.max
                    }, extraParams),
                    success: function (data) {
                        var parsed = options.parse && options.parse(data) || parse(data);
                        cache.add(term, parsed);
                        success(term, parsed);
                    }
                });
            } else {
                select.emptyList();
                failure(term);
            }
        };

        function parse(data) {
            var parsed = [];
            var rows = data.split("\n");
            for (var i = 0; i < rows.length; i++) {
                var row = $.trim(rows[i]);
                if (row) {
                    row = row.split("|");
                    parsed[parsed.length] = {
                        data: row,
                        value: row[0],
                        result: options.formatResult && options.formatResult(row, row[0]) || row[0]
                    };
                }
            }
            return parsed;
        };

        function stopLoading() {
            $input.removeClass(options.loadingClass);
        };
    };
    $.Autocompleter.defaults = {
        inputClass: "ac_input",
        resultsClass: "ac_results",
        loadingClass: "ac_loading",
        minChars: 1,
        delay: 400,
        matchCase: false,
        matchSubset: true,
        matchContains: false,
        cacheLength: 10,
        max: 100,
        mustMatch: false,
        extraParams: {},
        selectFirst: true,
        formatItem: function (row) {
            return row[0];
        },
        formatMatch: null,
        autoFill: false,
        width: 0,
        multiple: false,
        multipleSeparator: ", ",
        highlight: function (value, term) {
            return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
        },
        scroll: true,
        scrollHeight: 180
    };
    $.Autocompleter.Cache = function (options) {
        var data = {};
        var length = 0;

        function matchSubset(s, sub) {
            if (!options.matchCase) s = s.toLowerCase();
            var i = s.indexOf(sub);
            if (options.matchContains == "word") {
                i = s.toLowerCase().search("\\b" + sub.toLowerCase());
            }
            if (i == -1) return false;
            return i == 0 || options.matchContains;
        };

        function add(q, value) {
            if (length > options.cacheLength) {
                flush();
            }
            if (!data[q]) {
                length++;
            }
            data[q] = value;
        }

        function populate() {
            if (!options.data) return false;
            var stMatchSets = {},
                nullData = 0;
            if (!options.url) options.cacheLength = 1;
            stMatchSets[""] = [];
            for (var i = 0, ol = options.data.length; i < ol; i++) {
                var rawValue = options.data[i];
                rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;
                var value = options.formatMatch(rawValue, i + 1, options.data.length);
                if (value === false) continue;
                var firstChar = value.charAt(0).toLowerCase();
                if (!stMatchSets[firstChar]) stMatchSets[firstChar] = [];
                var row = {
                    value: value,
                    data: rawValue,
                    result: options.formatResult && options.formatResult(rawValue) || value
                };
                stMatchSets[firstChar].push(row);
                if (nullData++ < options.max) {
                    stMatchSets[""].push(row);
                }
            };
            $.each(stMatchSets, function (i, value) {
                options.cacheLength++;
                add(i, value);
            });
        }
        setTimeout(populate, 25);

        function flush() {
            data = {};
            length = 0;
        }
        return {
            flush: flush,
            add: add,
            populate: populate,
            load: function (q) {
                if (!options.cacheLength || !length) return null;
                if (!options.url && options.matchContains) {
                    var csub = [];
                    for (var k in data) {
                        if (k.length > 0) {
                            var c = data[k];
                            $.each(c, function (i, x) {
                                if (matchSubset(x.value, q)) {
                                    csub.push(x);
                                }
                            });
                        }
                    }
                    return csub;
                } else if (data[q]) {
                    return data[q];
                } else if (options.matchSubset) {
                    for (var i = q.length - 1; i >= options.minChars; i--) {
                        var c = data[q.substr(0, i)];
                        if (c) {
                            var csub = [];
                            $.each(c, function (i, x) {
                                if (matchSubset(x.value, q)) {
                                    csub[csub.length] = x;
                                }
                            });
                            return csub;
                        }
                    }
                }
                return null;
            }
        };
    };
    $.Autocompleter.Select = function (options, input, select, config) {
        var CLASSES = {
            ACTIVE: "ac_over"
        };
        var listItems, active = -1,
            data, term = "",
            needsInit = true,
            element, list;

        function init() {
            if (!needsInit) return;
            element = $("<div/>").hide().addClass(options.resultsClass).css("position", "absolute").appendTo(document.body);
            list = $("<ul/>").appendTo(element).mouseover(function (event) {
                if (target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {
                    active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
                    $(target(event)).addClass(CLASSES.ACTIVE);
                }
            }).click(function (event) {
                $(target(event)).addClass(CLASSES.ACTIVE);
                select();
                input.focus();
                return false;
            }).mousedown(function () {
                config.mouseDownOnSelect = true;
            }).mouseup(function () {
                config.mouseDownOnSelect = false;
            });
            if (options.width > 0) element.css("width", options.width);
            needsInit = false;
        }

        function target(event) {
            var element = event.target;
            while (element && element.tagName != "LI")
            element = element.parentNode;
            if (!element) return [];
            return element;
        }

        function moveSelect(step) {
            listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);
            movePosition(step);
            var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
            if (options.scroll) {
                var offset = 0;
                listItems.slice(0, active).each(function () {
                    offset += this.offsetHeight;
                });
                if ((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) {
                    list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight());
                } else if (offset < list.scrollTop()) {
                    list.scrollTop(offset);
                }
            }
        };

        function movePosition(step) {
            active += step;
            if (active < 0) {
                active = listItems.size() - 1;
            } else if (active >= listItems.size()) {
                active = 0;
            }
        }

        function limitNumberOfItems(available) {
            return options.max && options.max < available ? options.max : available;
        }

        function fillList() {
            list.empty();
            var max = limitNumberOfItems(data.length);
            for (var i = 0; i < max; i++) {
                if (!data[i]) continue;
                var formatted = options.formatItem(data[i].data, i + 1, max, data[i].value, term);
                if (formatted === false) continue;
                var li = $("<li/>").html(options.highlight(formatted, term)).addClass(i % 2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0];
                $.data(li, "ac_data", data[i]);
            }
            listItems = list.find("li");
            if (options.selectFirst) {
                listItems.slice(0, 1).addClass(CLASSES.ACTIVE);
                active = 0;
            }
            if ($.fn.bgiframe) list.bgiframe();
        }
        return {
            display: function (d, q) {
                init();
                data = d;
                term = q;
                fillList();
            },
            next: function () {
                moveSelect(1);
            },
            prev: function () {
                moveSelect(-1);
            },
            pageUp: function () {
                if (active != 0 && active - 8 < 0) {
                    moveSelect(-active);
                } else {
                    moveSelect(-8);
                }
            },
            pageDown: function () {
                if (active != listItems.size() - 1 && active + 8 > listItems.size()) {
                    moveSelect(listItems.size() - 1 - active);
                } else {
                    moveSelect(8);
                }
            },
            hide: function () {
                element && element.hide();
                listItems && listItems.removeClass(CLASSES.ACTIVE);
                active = -1;
            },
            visible: function () {
                return element && element.is(":visible");
            },
            current: function () {
                return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]);
            },
            show: function () {
                var offset = $(input).offset();
                element.css({
                    width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(),
                    top: offset.top + input.offsetHeight,
                    left: offset.left
                }).show();
                if (options.scroll) {
                    list.scrollTop(0);
                    list.css({
                        maxHeight: options.scrollHeight,
                        overflow: 'auto'
                    });
                    if ($.browser.msie && typeof document.body.style.maxHeight === "undefined") {
                        var listHeight = 0;
                        listItems.each(function () {
                            listHeight += this.offsetHeight;
                        });
                        var scrollbarsVisible = listHeight > options.scrollHeight;
                        list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight);
                        if (!scrollbarsVisible) {
                            listItems.width(list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")));
                        }
                    }
                }
            },
            selected: function () {
                var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);
                return selected && selected.length && $.data(selected[0], "ac_data");
            },
            emptyList: function () {
                list && list.empty();
            },
            unbind: function () {
                element && element.remove();
            }
        };
    };
    $.fn.selection = function (start, end) {
        if (start !== undefined) {
            return this.each(function () {
                if (this.createTextRange) {
                    var selRange = this.createTextRange();
                    if (end === undefined || start == end) {
                        selRange.move("character", start);
                        selRange.select();
                    } else {
                        selRange.collapse(true);
                        selRange.moveStart("character", start);
                        selRange.moveEnd("character", end);
                        selRange.select();
                    }
                } else if (this.setSelectionRange) {
                    this.setSelectionRange(start, end);
                } else if (this.selectionStart) {
                    this.selectionStart = start;
                    this.selectionEnd = end;
                }
            });
        }
        var field = this[0];
        if (field.createTextRange) {
            var range = document.selection.createRange(),
                orig = field.value,
                teststring = "<->",
                textLength = range.text.length;
            range.text = teststring;
            var caretAt = field.value.indexOf(teststring);
            field.value = orig;
            this.selection(caretAt, caretAt + textLength);
            return {
                start: caretAt,
                end: caretAt + textLength
            }
        } else if (field.selectionStart !== undefined) {
            return {
                start: field.selectionStart,
                end: field.selectionEnd
            }
        }
    };
})(jQuery);;
(function ($) {
    $.fn.ajaxSubmit = function (options) {
        if (!this.length) {
            log('ajaxSubmit: skipping submit process - no element selected');
            return this;
        }
        if (typeof options == 'function') {
            options = {
                success: options
            };
        }
        var url = $.trim(this.attr('action'));
        if (url) {
            url = (url.match(/^([^#]+)/) || [])[1];
        }
        url = url || window.location.href || '';
        options = $.extend(true, {
            url: url,
            type: this.attr('method') || 'GET',
            iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
        }, options);
        var veto = {};
        this.trigger('form-pre-serialize', [this, options, veto]);
        if (veto.veto) {
            log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
            return this;
        }
        if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
            log('ajaxSubmit: submit aborted via beforeSerialize callback');
            return this;
        }
        var n, v, a = this.formToArray(options.semantic);
        if (options.data) {
            options.extraData = options.data;
            for (n in options.data) {
                if (options.data[n] instanceof Array) {
                    for (var k in options.data[n]) {
                        a.push({
                            name: n,
                            value: options.data[n][k]
                        });
                    }
                } else {
                    v = options.data[n];
                    v = $.isFunction(v) ? v() : v;
                    a.push({
                        name: n,
                        value: v
                    });
                }
            }
        }
        if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
            log('ajaxSubmit: submit aborted via beforeSubmit callback');
            return this;
        }
        this.trigger('form-submit-validate', [a, this, options, veto]);
        if (veto.veto) {
            log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
            return this;
        }
        var q = $.param(a);
        if (options.type.toUpperCase() == 'GET') {
            options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
            options.data = null;
        } else {
            options.data = q;
        }
        var $form = this,
            callbacks = [];
        if (options.resetForm) {
            callbacks.push(function () {
                $form.resetForm();
            });
        }
        if (options.clearForm) {
            callbacks.push(function () {
                $form.clearForm();
            });
        }
        if (!options.dataType && options.target) {
            var oldSuccess = options.success ||
            function () {};
            callbacks.push(function (data) {
                var fn = options.replaceTarget ? 'replaceWith' : 'html';
                $(options.target)[fn](data).each(oldSuccess, arguments);
            });
        } else if (options.success) {
            callbacks.push(options.success);
        }
        options.success = function (data, status, xhr) {
            var context = options.context || options;
            for (var i = 0, max = callbacks.length; i < max; i++) {
                callbacks[i].apply(context, [data, status, xhr || $form, $form]);
            }
        };
        var fileInputs = $('input:file', this).length > 0;
        var mp = 'multipart/form-data';
        var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
        if (options.iframe !== false && (fileInputs || options.iframe || multipart)) {
            if (options.closeKeepAlive) {
                $.get(options.closeKeepAlive, fileUpload);
            } else {
                fileUpload();
            }
        } else {
            $.ajax(options);
        }
        this.trigger('form-submit-notify', [this, options]);
        return this;

        function fileUpload() {
            var form = $form[0];
            if ($(':input[name=submit],:input[id=submit]', form).length) {
                alert('Error: Form elements must not have name or id of "submit".');
                return;
            }
            var s = $.extend(true, {}, $.ajaxSettings, options);
            s.context = s.context || s;
            var id = 'jqFormIO' + (new Date().getTime()),
                fn = '_' + id;
            window[fn] = function () {
                var f = $io.data('form-plugin-onload');
                if (f) {
                    f();
                    window[fn] = undefined;
                    try {
                        delete window[fn];
                    } catch (e) {}
                }
            }
            var $io = $('<iframe id="' + id + '" name="' + id + '" src="' + s.iframeSrc + '" onload="window[\'_\'+this.id]()" />');
            var io = $io[0];
            $io.css({
                position: 'absolute',
                top: '-1000px',
                left: '-1000px'
            });
            var xhr = {
                aborted: 0,
                responseText: null,
                responseXML: null,
                status: 0,
                statusText: 'n/a',
                getAllResponseHeaders: function () {},
                getResponseHeader: function () {},
                setRequestHeader: function () {},
                abort: function () {
                    this.aborted = 1;
                    $io.attr('src', s.iframeSrc);
                }
            };
            var g = s.global;
            if (g && !$.active++) {
                $.event.trigger("ajaxStart");
            }
            if (g) {
                $.event.trigger("ajaxSend", [xhr, s]);
            }
            if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
                if (s.global) {
                    $.active--;
                }
                return;
            }
            if (xhr.aborted) {
                return;
            }
            var cbInvoked = false;
            var timedOut = 0;
            var sub = form.clk;
            if (sub) {
                var n = sub.name;
                if (n && !sub.disabled) {
                    s.extraData = s.extraData || {};
                    s.extraData[n] = sub.value;
                    if (sub.type == "image") {
                        s.extraData[n + '.x'] = form.clk_x;
                        s.extraData[n + '.y'] = form.clk_y;
                    }
                }
            }

            function doSubmit() {
                var t = $form.attr('target'),
                    a = $form.attr('action');
                form.setAttribute('target', id);
                if (form.getAttribute('method') != 'POST') {
                    form.setAttribute('method', 'POST');
                }
                if (form.getAttribute('action') != s.url) {
                    form.setAttribute('action', s.url);
                }
                if (!s.skipEncodingOverride) {
                    $form.attr({
                        encoding: 'multipart/form-data',
                        enctype: 'multipart/form-data'
                    });
                }
                if (s.timeout) {
                    setTimeout(function () {
                        timedOut = true;
                        cb();
                    }, s.timeout);
                }
                var extraInputs = [];
                try {
                    if (s.extraData) {
                        for (var n in s.extraData) {
                            extraInputs.push($('<input type="hidden" name="' + n + '" value="' + s.extraData[n] + '" />').appendTo(form)[0]);
                        }
                    }
                    $io.appendTo('body');
                    $io.data('form-plugin-onload', cb);
                    form.submit();
                } finally {
                    form.setAttribute('action', a);
                    if (t) {
                        form.setAttribute('target', t);
                    } else {
                        $form.removeAttr('target');
                    }
                    $(extraInputs).remove();
                }
            }
            if (s.forceSync) {
                doSubmit();
            } else {
                setTimeout(doSubmit, 10);
            }
            var data, doc, domCheckCount = 50;

            function cb() {
                if (cbInvoked) {
                    return;
                }
                $io.removeData('form-plugin-onload');
                var ok = true;
                try {
                    if (timedOut) {
                        throw 'timeout';
                    }
                    doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
                    var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
                    log('isXml=' + isXml);
                    if (!isXml && window.opera && (doc.body == null || doc.body.innerHTML == '')) {
                        if (--domCheckCount) {
                            log('requeing onLoad callback, DOM not available');
                            setTimeout(cb, 250);
                            return;
                        }
                    }
                    cbInvoked = true;
                    xhr.responseText = doc.documentElement ? doc.documentElement.innerHTML : null;
                    xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
                    xhr.getResponseHeader = function (header) {
                        var headers = {
                            'content-type': s.dataType
                        };
                        return headers[header];
                    };
                    var scr = /(json|script)/.test(s.dataType);
                    if (scr || s.textarea) {
                        var ta = doc.getElementsByTagName('textarea')[0];
                        if (ta) {
                            xhr.responseText = ta.value;
                        } else if (scr) {
                            var pre = doc.getElementsByTagName('pre')[0];
                            var b = doc.getElementsByTagName('body')[0];
                            if (pre) {
                                xhr.responseText = pre.innerHTML;
                            } else if (b) {
                                xhr.responseText = b.innerHTML;
                            }
                        }
                    } else if (s.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
                        xhr.responseXML = toXml(xhr.responseText);
                    }
                    data = $.httpData(xhr, s.dataType);
                } catch (e) {
                    log('error caught:', e);
                    ok = false;
                    xhr.error = e;
                    $.handleError(s, xhr, 'error', e);
                }
                if (ok) {
                    s.success.call(s.context, data, 'success', xhr);
                    if (g) {
                        $.event.trigger("ajaxSuccess", [xhr, s]);
                    }
                }
                if (g) {
                    $.event.trigger("ajaxComplete", [xhr, s]);
                }
                if (g && !--$.active) {
                    $.event.trigger("ajaxStop");
                }
                if (s.complete) {
                    s.complete.call(s.context, xhr, ok ? 'success' : 'error');
                }
                setTimeout(function () {
                    $io.removeData('form-plugin-onload');
                    $io.remove();
                    xhr.responseXML = null;
                }, 100);
            }

            function toXml(s, doc) {
                if (window.ActiveXObject) {
                    doc = new ActiveXObject('Microsoft.XMLDOM');
                    doc.async = 'false';
                    doc.loadXML(s);
                } else {
                    doc = (new DOMParser()).parseFromString(s, 'text/xml');
                }
                return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc : null;
            }
        }
    };
    $.fn.ajaxForm = function (options) {
        if (this.length === 0) {
            var o = {
                s: this.selector,
                c: this.context
            };
            if (!$.isReady && o.s) {
                log('DOM not ready, queuing ajaxForm');
                $(function () {
                    $(o.s, o.c).ajaxForm(options);
                });
                return this;
            }
            log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
            return this;
        }
        return this.ajaxFormUnbind().bind('submit.form-plugin', function (e) {
            if (!e.isDefaultPrevented()) {
                e.preventDefault();
                $(this).ajaxSubmit(options);
            }
        }).bind('click.form-plugin', function (e) {
            var target = e.target;
            var $el = $(target);
            if (!($el.is(":submit,input:image"))) {
                var t = $el.closest(':submit');
                if (t.length == 0) {
                    return;
                }
                target = t[0];
            }
            var form = this;
            form.clk = target;
            if (target.type == 'image') {
                if (e.offsetX != undefined) {
                    form.clk_x = e.offsetX;
                    form.clk_y = e.offsetY;
                } else if (typeof $.fn.offset == 'function') {
                    var offset = $el.offset();
                    form.clk_x = e.pageX - offset.left;
                    form.clk_y = e.pageY - offset.top;
                } else {
                    form.clk_x = e.pageX - target.offsetLeft;
                    form.clk_y = e.pageY - target.offsetTop;
                }
            }
            setTimeout(function () {
                form.clk = form.clk_x = form.clk_y = null;
            }, 100);
        });
    };
    $.fn.ajaxFormUnbind = function () {
        return this.unbind('submit.form-plugin click.form-plugin');
    };
    $.fn.formToArray = function (semantic) {
        var a = [];
        if (this.length === 0) {
            return a;
        }
        var form = this[0];
        var els = semantic ? form.getElementsByTagName('*') : form.elements;
        if (!els) {
            return a;
        }
        var i, j, n, v, el, max, jmax;
        for (i = 0, max = els.length; i < max; i++) {
            el = els[i];
            n = el.name;
            if (!n) {
                continue;
            }
            if (semantic && form.clk && el.type == "image") {
                if (!el.disabled && form.clk == el) {
                    a.push({
                        name: n,
                        value: $(el).val()
                    });
                    a.push({
                        name: n + '.x',
                        value: form.clk_x
                    }, {
                        name: n + '.y',
                        value: form.clk_y
                    });
                }
                continue;
            }
            v = $.fieldValue(el, true);
            if (v && v.constructor == Array) {
                for (j = 0, jmax = v.length; j < jmax; j++) {
                    a.push({
                        name: n,
                        value: v[j]
                    });
                }
            } else if (v !== null && typeof v != 'undefined') {
                a.push({
                    name: n,
                    value: v
                });
            }
        }
        if (!semantic && form.clk) {
            var $input = $(form.clk),
                input = $input[0];
            n = input.name;
            if (n && !input.disabled && input.type == 'image') {
                a.push({
                    name: n,
                    value: $input.val()
                });
                a.push({
                    name: n + '.x',
                    value: form.clk_x
                }, {
                    name: n + '.y',
                    value: form.clk_y
                });
            }
        }
        return a;
    };
    $.fn.formSerialize = function (semantic) {
        return $.param(this.formToArray(semantic));
    };
    $.fn.fieldSerialize = function (successful) {
        var a = [];
        this.each(function () {
            var n = this.name;
            if (!n) {
                return;
            }
            var v = $.fieldValue(this, successful);
            if (v && v.constructor == Array) {
                for (var i = 0, max = v.length; i < max; i++) {
                    a.push({
                        name: n,
                        value: v[i]
                    });
                }
            } else if (v !== null && typeof v != 'undefined') {
                a.push({
                    name: this.name,
                    value: v
                });
            }
        });
        return $.param(a);
    };
    $.fn.fieldValue = function (successful) {
        for (var val = [], i = 0, max = this.length; i < max; i++) {
            var el = this[i];
            var v = $.fieldValue(el, successful);
            if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
                continue;
            }
            v.constructor == Array ? $.merge(val, v) : val.push(v);
        }
        return val;
    };
    $.fieldValue = function (el, successful) {
        var n = el.name,
            t = el.type,
            tag = el.tagName.toLowerCase();
        if (successful === undefined) {
            successful = true;
        }
        if (successful && (!n || el.disabled || t == 'reset' || t == 'button' || (t == 'checkbox' || t == 'radio') && !el.checked || (t == 'submit' || t == 'image') && el.form && el.form.clk != el || tag == 'select' && el.selectedIndex == -1)) {
            return null;
        }
        if (tag == 'select') {
            var index = el.selectedIndex;
            if (index < 0) {
                return null;
            }
            var a = [],
                ops = el.options;
            var one = (t == 'select-one');
            var max = (one ? index + 1 : ops.length);
            for (var i = (one ? index : 0); i < max; i++) {
                var op = ops[i];
                if (op.selected) {
                    var v = op.value;
                    if (!v) {
                        v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
                    }
                    if (one) {
                        return v;
                    }
                    a.push(v);
                }
            }
            return a;
        }
        return $(el).val();
    };
    $.fn.clearForm = function () {
        return this.each(function () {
            $('input,select,textarea', this).clearFields();
        });
    };
    $.fn.clearFields = $.fn.clearInputs = function () {
        return this.each(function () {
            var t = this.type,
                tag = this.tagName.toLowerCase();
            if (t == 'text' || t == 'password' || tag == 'textarea') {
                this.value = '';
            } else if (t == 'checkbox' || t == 'radio') {
                this.checked = false;
            } else if (tag == 'select') {
                this.selectedIndex = -1;
            }
        });
    };
    $.fn.resetForm = function () {
        return this.each(function () {
            if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
                this.reset();
            }
        });
    };
    $.fn.enable = function (b) {
        if (b === undefined) {
            b = true;
        }
        return this.each(function () {
            this.disabled = !b;
        });
    };
    $.fn.selected = function (select) {
        if (select === undefined) {
            select = true;
        }
        return this.each(function () {
            var t = this.type;
            if (t == 'checkbox' || t == 'radio') {
                this.checked = select;
            } else if (this.tagName.toLowerCase() == 'option') {
                var $sel = $(this).parent('select');
                if (select && $sel[0] && $sel[0].type == 'select-one') {
                    $sel.find('option').selected(false);
                }
                this.selected = select;
            }
        });
    };

    function log() {
        if ($.fn.ajaxSubmit.debug) {
            var msg = '[jquery.form] ' + Array.prototype.join.call(arguments, '');
            if (window.console && window.console.log) {
                window.console.log(msg);
            } else if (window.opera && window.opera.postError) {
                window.opera.postError(msg);
            }
        }
    };
})(jQuery);

﻿
jQuery.fn.extend({
    everyTime: function (interval, label, fn, times) {
        return this.each(function () {
            jQuery.timer.add(this, interval, label, fn, times);
        });
    },
    oneTime: function (interval, label, fn) {
        return this.each(function () {
            jQuery.timer.add(this, interval, label, fn, 1);
        });
    },
    stopTime: function (label, fn) {
        return this.each(function () {
            jQuery.timer.remove(this, label, fn);
        });
    }
});
jQuery.extend({
    timer: {
        global: [],
        guid: 1,
        dataKey: "jQuery.timer",
        regex: /^([0-9]+(?:\.[0-9]*)?)\s*(.*s)?$/,
        powers: {
            'ms': 1,
            'cs': 10,
            'ds': 100,
            's': 1000,
            'das': 10000,
            'hs': 100000,
            'ks': 1000000
        },
        timeParse: function (value) {
            if (value == undefined || value == null) return null;
            var result = this.regex.exec(jQuery.trim(value.toString()));
            if (result[2]) {
                var num = parseFloat(result[1]);
                var mult = this.powers[result[2]] || 1;
                return num * mult;
            } else {
                return value;
            }
        },
        add: function (element, interval, label, fn, times) {
            var counter = 0;
            if (jQuery.isFunction(label)) {
                if (!times) times = fn;
                fn = label;
                label = interval;
            }
            interval = jQuery.timer.timeParse(interval);
            if (typeof interval != 'number' || isNaN(interval) || interval < 0) return;
            if (typeof times != 'number' || isNaN(times) || times < 0) times = 0;
            times = times || 0;
            var timers = jQuery.data(element, this.dataKey) || jQuery.data(element, this.dataKey, {});
            if (!timers[label]) timers[label] = {};
            fn.timerID = fn.timerID || this.guid++;
            var handler = function () {
                    if ((++counter > times && times !== 0) || fn.call(element, counter) === false) jQuery.timer.remove(element, label, fn);
                };
            handler.timerID = fn.timerID;
            if (!timers[label][fn.timerID]) timers[label][fn.timerID] = window.setInterval(handler, interval);
            this.global.push(element);
        },
        remove: function (element, label, fn) {
            var timers = jQuery.data(element, this.dataKey),
                ret;
            if (timers) {
                if (!label) {
                    for (label in timers)
                    this.remove(element, label, fn);
                } else if (timers[label]) {
                    if (fn) {
                        if (fn.timerID) {
                            window.clearInterval(timers[label][fn.timerID]);
                            delete timers[label][fn.timerID];
                        }
                    } else {
                        for (var fn in timers[label]) {
                            window.clearInterval(timers[label][fn]);
                            delete timers[label][fn];
                        }
                    }
                    for (ret in timers[label]) break;
                    if (!ret) {
                        ret = null;
                        delete timers[label];
                    }
                }
                for (ret in timers) break;
                if (!ret) jQuery.removeData(element, this.dataKey);
            }
        }
    }
});
jQuery(window).bind("unload", function () {
    jQuery.each(jQuery.timer.global, function (index, item) {
        jQuery.timer.remove(item);
    });
});

(function (c, j) {
    function k(a) {
        return !c(a).parents().andSelf().filter(function () {
            return c.curCSS(this, "visibility") === "hidden" || c.expr.filters.hidden(this)
        }).length
    }
    c.ui = c.ui || {};
    if (!c.ui.version) {
        c.extend(c.ui, {
            version: "1.8.8",
            keyCode: {
                ALT: 18,
                BACKSPACE: 8,
                CAPS_LOCK: 20,
                COMMA: 188,
                COMMAND: 91,
                COMMAND_LEFT: 91,
                COMMAND_RIGHT: 93,
                CONTROL: 17,
                DELETE: 46,
                DOWN: 40,
                END: 35,
                ENTER: 13,
                ESCAPE: 27,
                HOME: 36,
                INSERT: 45,
                LEFT: 37,
                MENU: 93,
                NUMPAD_ADD: 107,
                NUMPAD_DECIMAL: 110,
                NUMPAD_DIVIDE: 111,
                NUMPAD_ENTER: 108,
                NUMPAD_MULTIPLY: 106,
                NUMPAD_SUBTRACT: 109,
                PAGE_DOWN: 34,
                PAGE_UP: 33,
                PERIOD: 190,
                RIGHT: 39,
                SHIFT: 16,
                SPACE: 32,
                TAB: 9,
                UP: 38,
                WINDOWS: 91
            }
        });
        c.fn.extend({
            _focus: c.fn.focus,
            focus: function (a, b) {
                return typeof a === "number" ? this.each(function () {
                    var d = this;
                    setTimeout(function () {
                        c(d).focus();
                        b && b.call(d)
                    }, a)
                }) : this._focus.apply(this, arguments)
            },
            scrollParent: function () {
                var a;
                a = c.browser.msie && /(static|relative)/.test(this.css("position")) || /absolute/.test(this.css("position")) ? this.parents().filter(function () {
                    return /(relative|absolute|fixed)/.test(c.curCSS(this, "position", 1)) && /(auto|scroll)/.test(c.curCSS(this, "overflow", 1) + c.curCSS(this, "overflow-y", 1) + c.curCSS(this, "overflow-x", 1))
                }).eq(0) : this.parents().filter(function () {
                    return /(auto|scroll)/.test(c.curCSS(this, "overflow", 1) + c.curCSS(this, "overflow-y", 1) + c.curCSS(this, "overflow-x", 1))
                }).eq(0);
                return /fixed/.test(this.css("position")) || !a.length ? c(document) : a
            },
            zIndex: function (a) {
                if (a !== j) return this.css("zIndex", a);
                if (this.length) {
                    a = c(this[0]);
                    for (var b; a.length && a[0] !== document;) {
                        b = a.css("position");
                        if (b === "absolute" || b === "relative" || b === "fixed") {
                            b = parseInt(a.css("zIndex"), 10);
                            if (!isNaN(b) && b !== 0) return b
                        }
                        a = a.parent()
                    }
                }
                return 0
            },
            disableSelection: function () {
                return this.bind((c.support.selectstart ? "selectstart" : "mousedown") + ".ui-disableSelection", function (a) {
                    a.preventDefault()
                })
            },
            enableSelection: function () {
                return this.unbind(".ui-disableSelection")
            }
        });
        c.each(["Width", "Height"], function (a, b) {
            function d(f, g, l, m) {
                c.each(e, function () {
                    g -= parseFloat(c.curCSS(f, "padding" + this, true)) || 0;
                    if (l) g -= parseFloat(c.curCSS(f, "border" + this + "Width", true)) || 0;
                    if (m) g -= parseFloat(c.curCSS(f, "margin" + this, true)) || 0
                });
                return g
            }
            var e = b === "Width" ? ["Left", "Right"] : ["Top", "Bottom"],
                h = b.toLowerCase(),
                i = {
                    innerWidth: c.fn.innerWidth,
                    innerHeight: c.fn.innerHeight,
                    outerWidth: c.fn.outerWidth,
                    outerHeight: c.fn.outerHeight
                };
            c.fn["inner" + b] = function (f) {
                if (f === j) return i["inner" + b].call(this);
                return this.each(function () {
                    c(this).css(h, d(this, f) + "px")
                })
            };
            c.fn["outer" + b] = function (f, g) {
                if (typeof f !== "number") return i["outer" + b].call(this, f);
                return this.each(function () {
                    c(this).css(h, d(this, f, true, g) + "px")
                })
            }
        });
        c.extend(c.expr[":"], {
            data: function (a, b, d) {
                return !!c.data(a, d[3])
            },
            focusable: function (a) {
                var b = a.nodeName.toLowerCase(),
                    d = c.attr(a, "tabindex");
                if ("area" === b) {
                    b = a.parentNode;
                    d = b.name;
                    if (!a.href || !d || b.nodeName.toLowerCase() !== "map") return false;
                    a = c("img[usemap=#" + d + "]")[0];
                    return !!a && k(a)
                }
                return (/input|select|textarea|button|object/.test(b) ? !a.disabled : "a" == b ? a.href || !isNaN(d) : !isNaN(d)) && k(a)
            },
            tabbable: function (a) {
                var b = c.attr(a, "tabindex");
                return (isNaN(b) || b >= 0) && c(a).is(":focusable")
            }
        });
        c(function () {
            var a = document.body,
                b = a.appendChild(b = document.createElement("div"));
            c.extend(b.style, {
                minHeight: "100px",
                height: "auto",
                padding: 0,
                borderWidth: 0
            });
            c.support.minHeight = b.offsetHeight === 100;
            c.support.selectstart = "onselectstart" in b;
            a.removeChild(b).style.display = "none"
        });
        c.extend(c.ui, {
            plugin: {
                add: function (a, b, d) {
                    a = c.ui[a].prototype;
                    for (var e in d) {
                        a.plugins[e] = a.plugins[e] || [];
                        a.plugins[e].push([b, d[e]])
                    }
                },
                call: function (a, b, d) {
                    if ((b = a.plugins[b]) && a.element[0].parentNode) for (var e = 0; e < b.length; e++) a.options[b[e][0]] && b[e][1].apply(a.element, d)
                }
            },
            contains: function (a, b) {
                return document.compareDocumentPosition ? a.compareDocumentPosition(b) & 16 : a !== b && a.contains(b)
            },
            hasScroll: function (a, b) {
                if (c(a).css("overflow") === "hidden") return false;
                b = b && b === "left" ? "scrollLeft" : "scrollTop";
                var d = false;
                if (a[b] > 0) return true;
                a[b] = 1;
                d = a[b] > 0;
                a[b] = 0;
                return d
            },
            isOverAxis: function (a, b, d) {
                return a > b && a < b + d
            },
            isOver: function (a, b, d, e, h, i) {
                return c.ui.isOverAxis(a, d, h) && c.ui.isOverAxis(b, e, i)
            }
        })
    }
})(jQuery);;
(function (b, j) {
    if (b.cleanData) {
        var k = b.cleanData;
        b.cleanData = function (a) {
            for (var c = 0, d;
            (d = a[c]) != null; c++) b(d).triggerHandler("remove");
            k(a)
        }
    } else {
        var l = b.fn.remove;
        b.fn.remove = function (a, c) {
            return this.each(function () {
                if (!c) if (!a || b.filter(a, [this]).length) b("*", this).add([this]).each(function () {
                    b(this).triggerHandler("remove")
                });
                return l.call(b(this), a, c)
            })
        }
    }
    b.widget = function (a, c, d) {
        var e = a.split(".")[0],
            f;
        a = a.split(".")[1];
        f = e + "-" + a;
        if (!d) {
            d = c;
            c = b.Widget
        }
        b.expr[":"][f] = function (h) {
            return !!b.data(h, a)
        };
        b[e] = b[e] || {};
        b[e][a] = function (h, g) {
            arguments.length && this._createWidget(h, g)
        };
        c = new c;
        c.options = b.extend(true, {}, c.options);
        b[e][a].prototype = b.extend(true, c, {
            namespace: e,
            widgetName: a,
            widgetEventPrefix: b[e][a].prototype.widgetEventPrefix || a,
            widgetBaseClass: f
        }, d);
        b.widget.bridge(a, b[e][a])
    };
    b.widget.bridge = function (a, c) {
        b.fn[a] = function (d) {
            var e = typeof d === "string",
                f = Array.prototype.slice.call(arguments, 1),
                h = this;
            d = !e && f.length ? b.extend.apply(null, [true, d].concat(f)) : d;
            if (e && d.charAt(0) === "_") return h;
            e ? this.each(function () {
                var g = b.data(this, a),
                    i = g && b.isFunction(g[d]) ? g[d].apply(g, f) : g;
                if (i !== g && i !== j) {
                    h = i;
                    return false
                }
            }) : this.each(function () {
                var g = b.data(this, a);
                g ? g.option(d || {})._init() : b.data(this, a, new c(d, this))
            });
            return h
        }
    };
    b.Widget = function (a, c) {
        arguments.length && this._createWidget(a, c)
    };
    b.Widget.prototype = {
        widgetName: "widget",
        widgetEventPrefix: "",
        options: {
            disabled: false
        },
        _createWidget: function (a, c) {
            b.data(c, this.widgetName, this);
            this.element = b(c);
            this.options = b.extend(true, {}, this.options, this._getCreateOptions(), a);
            var d = this;
            this.element.bind("remove." + this.widgetName, function () {
                d.destroy()
            });
            this._create();
            this._trigger("create");
            this._init()
        },
        _getCreateOptions: function () {
            return b.metadata && b.metadata.get(this.element[0])[this.widgetName]
        },
        _create: function () {},
        _init: function () {},
        destroy: function () {
            this.element.unbind("." + this.widgetName).removeData(this.widgetName);
            this.widget().unbind("." + this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass + "-disabled ui-state-disabled")
        },
        widget: function () {
            return this.element
        },
        option: function (a, c) {
            var d = a;
            if (arguments.length === 0) return b.extend({}, this.options);
            if (typeof a === "string") {
                if (c === j) return this.options[a];
                d = {};
                d[a] = c
            }
            this._setOptions(d);
            return this
        },
        _setOptions: function (a) {
            var c = this;
            b.each(a, function (d, e) {
                c._setOption(d, e)
            });
            return this
        },
        _setOption: function (a, c) {
            this.options[a] = c;
            if (a === "disabled") this.widget()[c ? "addClass" : "removeClass"](this.widgetBaseClass + "-disabled ui-state-disabled").attr("aria-disabled", c);
            return this
        },
        enable: function () {
            return this._setOption("disabled", false)
        },
        disable: function () {
            return this._setOption("disabled", true)
        },
        _trigger: function (a, c, d) {
            var e = this.options[a];
            c = b.Event(c);
            c.type = (a === this.widgetEventPrefix ? a : this.widgetEventPrefix + a).toLowerCase();
            d = d || {};
            if (c.originalEvent) {
                a = b.event.props.length;
                for (var f; a;) {
                    f = b.event.props[--a];
                    c[f] = c.originalEvent[f]
                }
            }
            this.element.trigger(c, d);
            return !(b.isFunction(e) && e.call(this.element[0], c, d) === false || c.isDefaultPrevented())
        }
    }
})(jQuery);;
(function (c) {
    c.widget("ui.mouse", {
        options: {
            cancel: ":input,option",
            distance: 1,
            delay: 0
        },
        _mouseInit: function () {
            var a = this;
            this.element.bind("mousedown." + this.widgetName, function (b) {
                return a._mouseDown(b)
            }).bind("click." + this.widgetName, function (b) {
                if (true === c.data(b.target, a.widgetName + ".preventClickEvent")) {
                    c.removeData(b.target, a.widgetName + ".preventClickEvent");
                    b.stopImmediatePropagation();
                    return false
                }
            });
            this.started = false
        },
        _mouseDestroy: function () {
            this.element.unbind("." + this.widgetName)
        },
        _mouseDown: function (a) {
            a.originalEvent = a.originalEvent || {};
            if (!a.originalEvent.mouseHandled) {
                this._mouseStarted && this._mouseUp(a);
                this._mouseDownEvent = a;
                var b = this,
                    e = a.which == 1,
                    f = typeof this.options.cancel == "string" ? c(a.target).parents().add(a.target).filter(this.options.cancel).length : false;
                if (!e || f || !this._mouseCapture(a)) return true;
                this.mouseDelayMet = !this.options.delay;
                if (!this.mouseDelayMet) this._mouseDelayTimer = setTimeout(function () {
                    b.mouseDelayMet = true
                }, this.options.delay);
                if (this._mouseDistanceMet(a) && this._mouseDelayMet(a)) {
                    this._mouseStarted = this._mouseStart(a) !== false;
                    if (!this._mouseStarted) {
                        a.preventDefault();
                        return true
                    }
                }
                this._mouseMoveDelegate = function (d) {
                    return b._mouseMove(d)
                };
                this._mouseUpDelegate = function (d) {
                    return b._mouseUp(d)
                };
                c(document).bind("mousemove." + this.widgetName, this._mouseMoveDelegate).bind("mouseup." + this.widgetName, this._mouseUpDelegate);
                a.preventDefault();
                return a.originalEvent.mouseHandled = true
            }
        },
        _mouseMove: function (a) {
            if (c.browser.msie && !(document.documentMode >= 9) && !a.button) return this._mouseUp(a);
            if (this._mouseStarted) {
                this._mouseDrag(a);
                return a.preventDefault()
            }
            if (this._mouseDistanceMet(a) && this._mouseDelayMet(a))(this._mouseStarted = this._mouseStart(this._mouseDownEvent, a) !== false) ? this._mouseDrag(a) : this._mouseUp(a);
            return !this._mouseStarted
        },
        _mouseUp: function (a) {
            c(document).unbind("mousemove." + this.widgetName, this._mouseMoveDelegate).unbind("mouseup." + this.widgetName, this._mouseUpDelegate);
            if (this._mouseStarted) {
                this._mouseStarted = false;
                a.target == this._mouseDownEvent.target && c.data(a.target, this.widgetName + ".preventClickEvent", true);
                this._mouseStop(a)
            }
            return false
        },
        _mouseDistanceMet: function (a) {
            return Math.max(Math.abs(this._mouseDownEvent.pageX - a.pageX), Math.abs(this._mouseDownEvent.pageY - a.pageY)) >= this.options.distance
        },
        _mouseDelayMet: function () {
            return this.mouseDelayMet
        },
        _mouseStart: function () {},
        _mouseDrag: function () {},
        _mouseStop: function () {},
        _mouseCapture: function () {
            return true
        }
    })
})(jQuery);;
(function (c) {
    c.ui = c.ui || {};
    var n = /left|center|right/,
        o = /top|center|bottom/,
        t = c.fn.position,
        u = c.fn.offset;
    c.fn.position = function (b) {
        if (!b || !b.of) return t.apply(this, arguments);
        b = c.extend({}, b);
        var a = c(b.of),
            d = a[0],
            g = (b.collision || "flip").split(" "),
            e = b.offset ? b.offset.split(" ") : [0, 0],
            h, k, j;
        if (d.nodeType === 9) {
            h = a.width();
            k = a.height();
            j = {
                top: 0,
                left: 0
            }
        } else if (d.setTimeout) {
            h = a.width();
            k = a.height();
            j = {
                top: a.scrollTop(),
                left: a.scrollLeft()
            }
        } else if (d.preventDefault) {
            b.at = "left top";
            h = k = 0;
            j = {
                top: b.of.pageY,
                left: b.of.pageX
            }
        } else {
            h = a.outerWidth();
            k = a.outerHeight();
            j = a.offset()
        }
        c.each(["my", "at"], function () {
            var f = (b[this] || "").split(" ");
            if (f.length === 1) f = n.test(f[0]) ? f.concat(["center"]) : o.test(f[0]) ? ["center"].concat(f) : ["center", "center"];
            f[0] = n.test(f[0]) ? f[0] : "center";
            f[1] = o.test(f[1]) ? f[1] : "center";
            b[this] = f
        });
        if (g.length === 1) g[1] = g[0];
        e[0] = parseInt(e[0], 10) || 0;
        if (e.length === 1) e[1] = e[0];
        e[1] = parseInt(e[1], 10) || 0;
        if (b.at[0] === "right") j.left += h;
        else if (b.at[0] === "center") j.left += h / 2;
        if (b.at[1] === "bottom") j.top += k;
        else if (b.at[1] === "center") j.top += k / 2;
        j.left += e[0];
        j.top += e[1];
        return this.each(function () {
            var f = c(this),
                l = f.outerWidth(),
                m = f.outerHeight(),
                p = parseInt(c.curCSS(this, "marginLeft", true)) || 0,
                q = parseInt(c.curCSS(this, "marginTop", true)) || 0,
                v = l + p + (parseInt(c.curCSS(this, "marginRight", true)) || 0),
                w = m + q + (parseInt(c.curCSS(this, "marginBottom", true)) || 0),
                i = c.extend({}, j),
                r;
            if (b.my[0] === "right") i.left -= l;
            else if (b.my[0] === "center") i.left -= l / 2;
            if (b.my[1] === "bottom") i.top -= m;
            else if (b.my[1] === "center") i.top -= m / 2;
            i.left = Math.round(i.left);
            i.top = Math.round(i.top);
            r = {
                left: i.left - p,
                top: i.top - q
            };
            c.each(["left", "top"], function (s, x) {
                c.ui.position[g[s]] && c.ui.position[g[s]][x](i, {
                    targetWidth: h,
                    targetHeight: k,
                    elemWidth: l,
                    elemHeight: m,
                    collisionPosition: r,
                    collisionWidth: v,
                    collisionHeight: w,
                    offset: e,
                    my: b.my,
                    at: b.at
                })
            });
            c.fn.bgiframe && f.bgiframe();
            f.offset(c.extend(i, {
                using: b.using
            }))
        })
    };
    c.ui.position = {
        fit: {
            left: function (b, a) {
                var d = c(window);
                d = a.collisionPosition.left + a.collisionWidth - d.width() - d.scrollLeft();
                b.left = d > 0 ? b.left - d : Math.max(b.left - a.collisionPosition.left, b.left)
            },
            top: function (b, a) {
                var d = c(window);
                d = a.collisionPosition.top + a.collisionHeight - d.height() - d.scrollTop();
                b.top = d > 0 ? b.top - d : Math.max(b.top - a.collisionPosition.top, b.top)
            }
        },
        flip: {
            left: function (b, a) {
                if (a.at[0] !== "center") {
                    var d = c(window);
                    d = a.collisionPosition.left + a.collisionWidth - d.width() - d.scrollLeft();
                    var g = a.my[0] === "left" ? -a.elemWidth : a.my[0] === "right" ? a.elemWidth : 0,
                        e = a.at[0] === "left" ? a.targetWidth : -a.targetWidth,
                        h = -2 * a.offset[0];
                    b.left += a.collisionPosition.left < 0 ? g + e + h : d > 0 ? g + e + h : 0
                }
            },
            top: function (b, a) {
                if (a.at[1] !== "center") {
                    var d = c(window);
                    d = a.collisionPosition.top + a.collisionHeight - d.height() - d.scrollTop();
                    var g = a.my[1] === "top" ? -a.elemHeight : a.my[1] === "bottom" ? a.elemHeight : 0,
                        e = a.at[1] === "top" ? a.targetHeight : -a.targetHeight,
                        h = -2 * a.offset[1];
                    b.top += a.collisionPosition.top < 0 ? g + e + h : d > 0 ? g + e + h : 0
                }
            }
        }
    };
    if (!c.offset.setOffset) {
        c.offset.setOffset = function (b, a) {
            if (/static/.test(c.curCSS(b, "position"))) b.style.position = "relative";
            var d = c(b),
                g = d.offset(),
                e = parseInt(c.curCSS(b, "top", true), 10) || 0,
                h = parseInt(c.curCSS(b, "left", true), 10) || 0;
            g = {
                top: a.top - g.top + e,
                left: a.left - g.left + h
            };
            "using" in a ? a.using.call(b, g) : d.css(g)
        };
        c.fn.offset = function (b) {
            var a = this[0];
            if (!a || !a.ownerDocument) return null;
            if (b) return this.each(function () {
                c.offset.setOffset(this, b)
            });
            return u.call(this)
        }
    }
})(jQuery);;
(function (d) {
    d.widget("ui.draggable", d.ui.mouse, {
        widgetEventPrefix: "drag",
        options: {
            addClasses: true,
            appendTo: "parent",
            axis: false,
            connectToSortable: false,
            containment: false,
            cursor: "auto",
            cursorAt: false,
            grid: false,
            handle: false,
            helper: "original",
            iframeFix: false,
            opacity: false,
            refreshPositions: false,
            revert: false,
            revertDuration: 500,
            scope: "default",
            scroll: true,
            scrollSensitivity: 20,
            scrollSpeed: 20,
            snap: false,
            snapMode: "both",
            snapTolerance: 20,
            stack: false,
            zIndex: false
        },
        _create: function () {
            if (this.options.helper == "original" && !/^(?:r|a|f)/.test(this.element.css("position"))) this.element[0].style.position = "relative";
            this.options.addClasses && this.element.addClass("ui-draggable");
            this.options.disabled && this.element.addClass("ui-draggable-disabled");
            this._mouseInit()
        },
        destroy: function () {
            if (this.element.data("draggable")) {
                this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");
                this._mouseDestroy();
                return this
            }
        },
        _mouseCapture: function (a) {
            var b = this.options;
            if (this.helper || b.disabled || d(a.target).is(".ui-resizable-handle")) return false;
            this.handle = this._getHandle(a);
            if (!this.handle) return false;
            return true
        },
        _mouseStart: function (a) {
            var b = this.options;
            this.helper = this._createHelper(a);
            this._cacheHelperProportions();
            if (d.ui.ddmanager) d.ui.ddmanager.current = this;
            this._cacheMargins();
            this.cssPosition = this.helper.css("position");
            this.scrollParent = this.helper.scrollParent();
            this.offset = this.positionAbs = this.element.offset();
            this.offset = {
                top: this.offset.top - this.margins.top,
                left: this.offset.left - this.margins.left
            };
            d.extend(this.offset, {
                click: {
                    left: a.pageX - this.offset.left,
                    top: a.pageY - this.offset.top
                },
                parent: this._getParentOffset(),
                relative: this._getRelativeOffset()
            });
            this.originalPosition = this.position = this._generatePosition(a);
            this.originalPageX = a.pageX;
            this.originalPageY = a.pageY;
            b.cursorAt && this._adjustOffsetFromHelper(b.cursorAt);
            b.containment && this._setContainment();
            if (this._trigger("start", a) === false) {
                this._clear();
                return false
            }
            this._cacheHelperProportions();
            d.ui.ddmanager && !b.dropBehaviour && d.ui.ddmanager.prepareOffsets(this, a);
            this.helper.addClass("ui-draggable-dragging");
            this._mouseDrag(a, true);
            return true
        },
        _mouseDrag: function (a, b) {
            this.position = this._generatePosition(a);
            this.positionAbs = this._convertPositionTo("absolute");
            if (!b) {
                b = this._uiHash();
                if (this._trigger("drag", a, b) === false) {
                    this._mouseUp({});
                    return false
                }
                this.position = b.position
            }
            if (!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left + "px";
            if (!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top + "px";
            d.ui.ddmanager && d.ui.ddmanager.drag(this, a);
            return false
        },
        _mouseStop: function (a) {
            var b = false;
            if (d.ui.ddmanager && !this.options.dropBehaviour) b = d.ui.ddmanager.drop(this, a);
            if (this.dropped) {
                b = this.dropped;
                this.dropped = false
            }
            if (!this.element[0] || !this.element[0].parentNode) return false;
            if (this.options.revert == "invalid" && !b || this.options.revert == "valid" && b || this.options.revert === true || d.isFunction(this.options.revert) && this.options.revert.call(this.element, b)) {
                var c = this;
                d(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function () {
                    c._trigger("stop", a) !== false && c._clear()
                })
            } else this._trigger("stop", a) !== false && this._clear();
            return false
        },
        cancel: function () {
            this.helper.is(".ui-draggable-dragging") ? this._mouseUp({}) : this._clear();
            return this
        },
        _getHandle: function (a) {
            var b = !this.options.handle || !d(this.options.handle, this.element).length ? true : false;
            d(this.options.handle, this.element).find("*").andSelf().each(function () {
                if (this == a.target) b = true
            });
            return b
        },
        _createHelper: function (a) {
            var b = this.options;
            a = d.isFunction(b.helper) ? d(b.helper.apply(this.element[0], [a])) : b.helper == "clone" ? this.element.clone() : this.element;
            a.parents("body").length || a.appendTo(b.appendTo == "parent" ? this.element[0].parentNode : b.appendTo);
            a[0] != this.element[0] && !/(fixed|absolute)/.test(a.css("position")) && a.css("position", "absolute");
            return a
        },
        _adjustOffsetFromHelper: function (a) {
            if (typeof a == "string") a = a.split(" ");
            if (d.isArray(a)) a = {
                left: +a[0],
                top: +a[1] || 0
            };
            if ("left" in a) this.offset.click.left = a.left + this.margins.left;
            if ("right" in a) this.offset.click.left = this.helperProportions.width - a.right + this.margins.left;
            if ("top" in a) this.offset.click.top = a.top + this.margins.top;
            if ("bottom" in a) this.offset.click.top = this.helperProportions.height - a.bottom + this.margins.top
        },
        _getParentOffset: function () {
            this.offsetParent = this.helper.offsetParent();
            var a = this.offsetParent.offset();
            if (this.cssPosition == "absolute" && this.scrollParent[0] != document && d.ui.contains(this.scrollParent[0], this.offsetParent[0])) {
                a.left += this.scrollParent.scrollLeft();
                a.top += this.scrollParent.scrollTop()
            }
            if (this.offsetParent[0] == document.body || this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == "html" && d.browser.msie) a = {
                top: 0,
                left: 0
            };
            return {
                top: a.top + (parseInt(this.offsetParent.css("borderTopWidth"), 10) || 0),
                left: a.left + (parseInt(this.offsetParent.css("borderLeftWidth"), 10) || 0)
            }
        },
        _getRelativeOffset: function () {
            if (this.cssPosition == "relative") {
                var a = this.element.position();
                return {
                    top: a.top - (parseInt(this.helper.css("top"), 10) || 0) + this.scrollParent.scrollTop(),
                    left: a.left - (parseInt(this.helper.css("left"), 10) || 0) + this.scrollParent.scrollLeft()
                }
            } else return {
                top: 0,
                left: 0
            }
        },
        _cacheMargins: function () {
            this.margins = {
                left: parseInt(this.element.css("marginLeft"), 10) || 0,
                top: parseInt(this.element.css("marginTop"), 10) || 0
            }
        },
        _cacheHelperProportions: function () {
            this.helperProportions = {
                width: this.helper.outerWidth(),
                height: this.helper.outerHeight()
            }
        },
        _setContainment: function () {
            var a = this.options;
            if (a.containment == "parent") a.containment = this.helper[0].parentNode;
            if (a.containment == "document" || a.containment == "window") this.containment = [(a.containment == "document" ? 0 : d(window).scrollLeft()) - this.offset.relative.left - this.offset.parent.left, (a.containment == "document" ? 0 : d(window).scrollTop()) - this.offset.relative.top - this.offset.parent.top, (a.containment == "document" ? 0 : d(window).scrollLeft()) + d(a.containment == "document" ? document : window).width() - this.helperProportions.width - this.margins.left, (a.containment == "document" ? 0 : d(window).scrollTop()) + (d(a.containment == "document" ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top];
            if (!/^(document|window|parent)$/.test(a.containment) && a.containment.constructor != Array) {
                var b = d(a.containment)[0];
                if (b) {
                    a = d(a.containment).offset();
                    var c = d(b).css("overflow") != "hidden";
                    this.containment = [a.left + (parseInt(d(b).css("borderLeftWidth"), 10) || 0) + (parseInt(d(b).css("paddingLeft"), 10) || 0) - this.margins.left, a.top + (parseInt(d(b).css("borderTopWidth"), 10) || 0) + (parseInt(d(b).css("paddingTop"), 10) || 0) - this.margins.top, a.left + (c ? Math.max(b.scrollWidth, b.offsetWidth) : b.offsetWidth) - (parseInt(d(b).css("borderLeftWidth"), 10) || 0) - (parseInt(d(b).css("paddingRight"), 10) || 0) - this.helperProportions.width - this.margins.left, a.top + (c ? Math.max(b.scrollHeight, b.offsetHeight) : b.offsetHeight) - (parseInt(d(b).css("borderTopWidth"), 10) || 0) - (parseInt(d(b).css("paddingBottom"), 10) || 0) - this.helperProportions.height - this.margins.top]
                }
            } else if (a.containment.constructor == Array) this.containment = a.containment
        },
        _convertPositionTo: function (a, b) {
            if (!b) b = this.position;
            a = a == "absolute" ? 1 : -1;
            var c = this.cssPosition == "absolute" && !(this.scrollParent[0] != document && d.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent,
                f = /(html|body)/i.test(c[0].tagName);
            return {
                top: b.top + this.offset.relative.top * a + this.offset.parent.top * a - (d.browser.safari && d.browser.version < 526 && this.cssPosition == "fixed" ? 0 : (this.cssPosition == "fixed" ? -this.scrollParent.scrollTop() : f ? 0 : c.scrollTop()) * a),
                left: b.left + this.offset.relative.left * a + this.offset.parent.left * a - (d.browser.safari && d.browser.version < 526 && this.cssPosition == "fixed" ? 0 : (this.cssPosition == "fixed" ? -this.scrollParent.scrollLeft() : f ? 0 : c.scrollLeft()) * a)
            }
        },
        _generatePosition: function (a) {
            var b = this.options,
                c = this.cssPosition == "absolute" && !(this.scrollParent[0] != document && d.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent,
                f = /(html|body)/i.test(c[0].tagName),
                e = a.pageX,
                g = a.pageY;
            if (this.originalPosition) {
                if (this.containment) {
                    if (a.pageX - this.offset.click.left < this.containment[0]) e = this.containment[0] + this.offset.click.left;
                    if (a.pageY - this.offset.click.top < this.containment[1]) g = this.containment[1] + this.offset.click.top;
                    if (a.pageX - this.offset.click.left > this.containment[2]) e = this.containment[2] + this.offset.click.left;
                    if (a.pageY - this.offset.click.top > this.containment[3]) g = this.containment[3] + this.offset.click.top
                }
                if (b.grid) {
                    g = this.originalPageY + Math.round((g - this.originalPageY) / b.grid[1]) * b.grid[1];
                    g = this.containment ? !(g - this.offset.click.top < this.containment[1] || g - this.offset.click.top > this.containment[3]) ? g : !(g - this.offset.click.top < this.containment[1]) ? g - b.grid[1] : g + b.grid[1] : g;
                    e = this.originalPageX + Math.round((e - this.originalPageX) / b.grid[0]) * b.grid[0];
                    e = this.containment ? !(e - this.offset.click.left < this.containment[0] || e - this.offset.click.left > this.containment[2]) ? e : !(e - this.offset.click.left < this.containment[0]) ? e - b.grid[0] : e + b.grid[0] : e
                }
            }
            return {
                top: g - this.offset.click.top - this.offset.relative.top - this.offset.parent.top + (d.browser.safari && d.browser.version < 526 && this.cssPosition == "fixed" ? 0 : this.cssPosition == "fixed" ? -this.scrollParent.scrollTop() : f ? 0 : c.scrollTop()),
                left: e - this.offset.click.left - this.offset.relative.left - this.offset.parent.left + (d.browser.safari && d.browser.version < 526 && this.cssPosition == "fixed" ? 0 : this.cssPosition == "fixed" ? -this.scrollParent.scrollLeft() : f ? 0 : c.scrollLeft())
            }
        },
        _clear: function () {
            this.helper.removeClass("ui-draggable-dragging");
            this.helper[0] != this.element[0] && !this.cancelHelperRemoval && this.helper.remove();
            this.helper = null;
            this.cancelHelperRemoval = false
        },
        _trigger: function (a, b, c) {
            c = c || this._uiHash();
            d.ui.plugin.call(this, a, [b, c]);
            if (a == "drag") this.positionAbs = this._convertPositionTo("absolute");
            return d.Widget.prototype._trigger.call(this, a, b, c)
        },
        plugins: {},
        _uiHash: function () {
            return {
                helper: this.helper,
                position: this.position,
                originalPosition: this.originalPosition,
                offset: this.positionAbs
            }
        }
    });
    d.extend(d.ui.draggable, {
        version: "1.8.8"
    });
    d.ui.plugin.add("draggable", "connectToSortable", {
        start: function (a, b) {
            var c = d(this).data("draggable"),
                f = c.options,
                e = d.extend({}, b, {
                    item: c.element
                });
            c.sortables = [];
            d(f.connectToSortable).each(function () {
                var g = d.data(this, "sortable");
                if (g && !g.options.disabled) {
                    c.sortables.push({
                        instance: g,
                        shouldRevert: g.options.revert
                    });
                    g._refreshItems();
                    g._trigger("activate", a, e)
                }
            })
        },
        stop: function (a, b) {
            var c = d(this).data("draggable"),
                f = d.extend({}, b, {
                    item: c.element
                });
            d.each(c.sortables, function () {
                if (this.instance.isOver) {
                    this.instance.isOver = 0;
                    c.cancelHelperRemoval = true;
                    this.instance.cancelHelperRemoval = false;
                    if (this.shouldRevert) this.instance.options.revert = true;
                    this.instance._mouseStop(a);
                    this.instance.options.helper = this.instance.options._helper;
                    c.options.helper == "original" && this.instance.currentItem.css({
                        top: "auto",
                        left: "auto"
                    })
                } else {
                    this.instance.cancelHelperRemoval = false;
                    this.instance._trigger("deactivate", a, f)
                }
            })
        },
        drag: function (a, b) {
            var c = d(this).data("draggable"),
                f = this;
            d.each(c.sortables, function () {
                this.instance.positionAbs = c.positionAbs;
                this.instance.helperProportions = c.helperProportions;
                this.instance.offset.click = c.offset.click;
                if (this.instance._intersectsWith(this.instance.containerCache)) {
                    if (!this.instance.isOver) {
                        this.instance.isOver = 1;
                        this.instance.currentItem = d(f).clone().appendTo(this.instance.element).data("sortable-item", true);
                        this.instance.options._helper = this.instance.options.helper;
                        this.instance.options.helper = function () {
                            return b.helper[0]
                        };
                        a.target = this.instance.currentItem[0];
                        this.instance._mouseCapture(a, true);
                        this.instance._mouseStart(a, true, true);
                        this.instance.offset.click.top = c.offset.click.top;
                        this.instance.offset.click.left = c.offset.click.left;
                        this.instance.offset.parent.left -= c.offset.parent.left - this.instance.offset.parent.left;
                        this.instance.offset.parent.top -= c.offset.parent.top - this.instance.offset.parent.top;
                        c._trigger("toSortable", a);
                        c.dropped = this.instance.element;
                        c.currentItem = c.element;
                        this.instance.fromOutside = c
                    }
                    this.instance.currentItem && this.instance._mouseDrag(a)
                } else if (this.instance.isOver) {
                    this.instance.isOver = 0;
                    this.instance.cancelHelperRemoval = true;
                    this.instance.options.revert = false;
                    this.instance._trigger("out", a, this.instance._uiHash(this.instance));
                    this.instance._mouseStop(a, true);
                    this.instance.options.helper = this.instance.options._helper;
                    this.instance.currentItem.remove();
                    this.instance.placeholder && this.instance.placeholder.remove();
                    c._trigger("fromSortable", a);
                    c.dropped = false
                }
            })
        }
    });
    d.ui.plugin.add("draggable", "cursor", {
        start: function () {
            var a = d("body"),
                b = d(this).data("draggable").options;
            if (a.css("cursor")) b._cursor = a.css("cursor");
            a.css("cursor", b.cursor)
        },
        stop: function () {
            var a = d(this).data("draggable").options;
            a._cursor && d("body").css("cursor", a._cursor)
        }
    });
    d.ui.plugin.add("draggable", "iframeFix", {
        start: function () {
            var a = d(this).data("draggable").options;
            d(a.iframeFix === true ? "iframe" : a.iframeFix).each(function () {
                d('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>').css({
                    width: this.offsetWidth + "px",
                    height: this.offsetHeight + "px",
                    position: "absolute",
                    opacity: "0.001",
                    zIndex: 1E3
                }).css(d(this).offset()).appendTo("body")
            })
        },
        stop: function () {
            d("div.ui-draggable-iframeFix").each(function () {
                this.parentNode.removeChild(this)
            })
        }
    });
    d.ui.plugin.add("draggable", "opacity", {
        start: function (a, b) {
            a = d(b.helper);
            b = d(this).data("draggable").options;
            if (a.css("opacity")) b._opacity = a.css("opacity");
            a.css("opacity", b.opacity)
        },
        stop: function (a, b) {
            a = d(this).data("draggable").options;
            a._opacity && d(b.helper).css("opacity", a._opacity)
        }
    });
    d.ui.plugin.add("draggable", "scroll", {
        start: function () {
            var a = d(this).data("draggable");
            if (a.scrollParent[0] != document && a.scrollParent[0].tagName != "HTML") a.overflowOffset = a.scrollParent.offset()
        },
        drag: function (a) {
            var b = d(this).data("draggable"),
                c = b.options,
                f = false;
            if (b.scrollParent[0] != document && b.scrollParent[0].tagName != "HTML") {
                if (!c.axis || c.axis != "x") if (b.overflowOffset.top + b.scrollParent[0].offsetHeight - a.pageY < c.scrollSensitivity) b.scrollParent[0].scrollTop = f = b.scrollParent[0].scrollTop + c.scrollSpeed;
                else if (a.pageY - b.overflowOffset.top < c.scrollSensitivity) b.scrollParent[0].scrollTop = f = b.scrollParent[0].scrollTop - c.scrollSpeed;
                if (!c.axis || c.axis != "y") if (b.overflowOffset.left + b.scrollParent[0].offsetWidth - a.pageX < c.scrollSensitivity) b.scrollParent[0].scrollLeft = f = b.scrollParent[0].scrollLeft + c.scrollSpeed;
                else if (a.pageX - b.overflowOffset.left < c.scrollSensitivity) b.scrollParent[0].scrollLeft = f = b.scrollParent[0].scrollLeft - c.scrollSpeed
            } else {
                if (!c.axis || c.axis != "x") if (a.pageY - d(document).scrollTop() < c.scrollSensitivity) f = d(document).scrollTop(d(document).scrollTop() - c.scrollSpeed);
                else if (d(window).height() - (a.pageY - d(document).scrollTop()) < c.scrollSensitivity) f = d(document).scrollTop(d(document).scrollTop() + c.scrollSpeed);
                if (!c.axis || c.axis != "y") if (a.pageX - d(document).scrollLeft() < c.scrollSensitivity) f = d(document).scrollLeft(d(document).scrollLeft() - c.scrollSpeed);
                else if (d(window).width() - (a.pageX - d(document).scrollLeft()) < c.scrollSensitivity) f = d(document).scrollLeft(d(document).scrollLeft() + c.scrollSpeed)
            }
            f !== false && d.ui.ddmanager && !c.dropBehaviour && d.ui.ddmanager.prepareOffsets(b, a)
        }
    });
    d.ui.plugin.add("draggable", "snap", {
        start: function () {
            var a = d(this).data("draggable"),
                b = a.options;
            a.snapElements = [];
            d(b.snap.constructor != String ? b.snap.items || ":data(draggable)" : b.snap).each(function () {
                var c = d(this),
                    f = c.offset();
                this != a.element[0] && a.snapElements.push({
                    item: this,
                    width: c.outerWidth(),
                    height: c.outerHeight(),
                    top: f.top,
                    left: f.left
                })
            })
        },
        drag: function (a, b) {
            for (var c = d(this).data("draggable"), f = c.options, e = f.snapTolerance, g = b.offset.left, n = g + c.helperProportions.width, m = b.offset.top, o = m + c.helperProportions.height, h = c.snapElements.length - 1; h >= 0; h--) {
                var i = c.snapElements[h].left,
                    k = i + c.snapElements[h].width,
                    j = c.snapElements[h].top,
                    l = j + c.snapElements[h].height;
                if (i - e < g && g < k + e && j - e < m && m < l + e || i - e < g && g < k + e && j - e < o && o < l + e || i - e < n && n < k + e && j - e < m && m < l + e || i - e < n && n < k + e && j - e < o && o < l + e) {
                    if (f.snapMode != "inner") {
                        var p = Math.abs(j - o) <= e,
                            q = Math.abs(l - m) <= e,
                            r = Math.abs(i - n) <= e,
                            s = Math.abs(k - g) <= e;
                        if (p) b.position.top = c._convertPositionTo("relative", {
                            top: j - c.helperProportions.height,
                            left: 0
                        }).top - c.margins.top;
                        if (q) b.position.top = c._convertPositionTo("relative", {
                            top: l,
                            left: 0
                        }).top - c.margins.top;
                        if (r) b.position.left = c._convertPositionTo("relative", {
                            top: 0,
                            left: i - c.helperProportions.width
                        }).left - c.margins.left;
                        if (s) b.position.left = c._convertPositionTo("relative", {
                            top: 0,
                            left: k
                        }).left - c.margins.left
                    }
                    var t = p || q || r || s;
                    if (f.snapMode != "outer") {
                        p = Math.abs(j - m) <= e;
                        q = Math.abs(l - o) <= e;
                        r = Math.abs(i - g) <= e;
                        s = Math.abs(k - n) <= e;
                        if (p) b.position.top = c._convertPositionTo("relative", {
                            top: j,
                            left: 0
                        }).top - c.margins.top;
                        if (q) b.position.top = c._convertPositionTo("relative", {
                            top: l - c.helperProportions.height,
                            left: 0
                        }).top - c.margins.top;
                        if (r) b.position.left = c._convertPositionTo("relative", {
                            top: 0,
                            left: i
                        }).left - c.margins.left;
                        if (s) b.position.left = c._convertPositionTo("relative", {
                            top: 0,
                            left: k - c.helperProportions.width
                        }).left - c.margins.left
                    }
                    if (!c.snapElements[h].snapping && (p || q || r || s || t)) c.options.snap.snap && c.options.snap.snap.call(c.element, a, d.extend(c._uiHash(), {
                        snapItem: c.snapElements[h].item
                    }));
                    c.snapElements[h].snapping = p || q || r || s || t
                } else {
                    c.snapElements[h].snapping && c.options.snap.release && c.options.snap.release.call(c.element, a, d.extend(c._uiHash(), {
                        snapItem: c.snapElements[h].item
                    }));
                    c.snapElements[h].snapping = false
                }
            }
        }
    });
    d.ui.plugin.add("draggable", "stack", {
        start: function () {
            var a = d(this).data("draggable").options;
            a = d.makeArray(d(a.stack)).sort(function (c, f) {
                return (parseInt(d(c).css("zIndex"), 10) || 0) - (parseInt(d(f).css("zIndex"), 10) || 0)
            });
            if (a.length) {
                var b = parseInt(a[0].style.zIndex) || 0;
                d(a).each(function (c) {
                    this.style.zIndex = b + c
                });
                this[0].style.zIndex = b + a.length
            }
        }
    });
    d.ui.plugin.add("draggable", "zIndex", {
        start: function (a, b) {
            a = d(b.helper);
            b = d(this).data("draggable").options;
            if (a.css("zIndex")) b._zIndex = a.css("zIndex");
            a.css("zIndex", b.zIndex)
        },
        stop: function (a, b) {
            a = d(this).data("draggable").options;
            a._zIndex && d(b.helper).css("zIndex", a._zIndex)
        }
    })
})(jQuery);;
(function (d) {
    d.widget("ui.droppable", {
        widgetEventPrefix: "drop",
        options: {
            accept: "*",
            activeClass: false,
            addClasses: true,
            greedy: false,
            hoverClass: false,
            scope: "default",
            tolerance: "intersect"
        },
        _create: function () {
            var a = this.options,
                b = a.accept;
            this.isover = 0;
            this.isout = 1;
            this.accept = d.isFunction(b) ? b : function (c) {
                return c.is(b)
            };
            this.proportions = {
                width: this.element[0].offsetWidth,
                height: this.element[0].offsetHeight
            };
            d.ui.ddmanager.droppables[a.scope] = d.ui.ddmanager.droppables[a.scope] || [];
            d.ui.ddmanager.droppables[a.scope].push(this);
            a.addClasses && this.element.addClass("ui-droppable")
        },
        destroy: function () {
            for (var a = d.ui.ddmanager.droppables[this.options.scope], b = 0; b < a.length; b++) a[b] == this && a.splice(b, 1);
            this.element.removeClass("ui-droppable ui-droppable-disabled").removeData("droppable").unbind(".droppable");
            return this
        },
        _setOption: function (a, b) {
            if (a == "accept") this.accept = d.isFunction(b) ? b : function (c) {
                return c.is(b)
            };
            d.Widget.prototype._setOption.apply(this, arguments)
        },
        _activate: function (a) {
            var b = d.ui.ddmanager.current;
            this.options.activeClass && this.element.addClass(this.options.activeClass);
            b && this._trigger("activate", a, this.ui(b))
        },
        _deactivate: function (a) {
            var b = d.ui.ddmanager.current;
            this.options.activeClass && this.element.removeClass(this.options.activeClass);
            b && this._trigger("deactivate", a, this.ui(b))
        },
        _over: function (a) {
            var b = d.ui.ddmanager.current;
            if (!(!b || (b.currentItem || b.element)[0] == this.element[0])) if (this.accept.call(this.element[0], b.currentItem || b.element)) {
                this.options.hoverClass && this.element.addClass(this.options.hoverClass);
                this._trigger("over", a, this.ui(b))
            }
        },
        _out: function (a) {
            var b = d.ui.ddmanager.current;
            if (!(!b || (b.currentItem || b.element)[0] == this.element[0])) if (this.accept.call(this.element[0], b.currentItem || b.element)) {
                this.options.hoverClass && this.element.removeClass(this.options.hoverClass);
                this._trigger("out", a, this.ui(b))
            }
        },
        _drop: function (a, b) {
            var c = b || d.ui.ddmanager.current;
            if (!c || (c.currentItem || c.element)[0] == this.element[0]) return false;
            var e = false;
            this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function () {
                var g = d.data(this, "droppable");
                if (g.options.greedy && !g.options.disabled && g.options.scope == c.options.scope && g.accept.call(g.element[0], c.currentItem || c.element) && d.ui.intersect(c, d.extend(g, {
                    offset: g.element.offset()
                }), g.options.tolerance)) {
                    e = true;
                    return false
                }
            });
            if (e) return false;
            if (this.accept.call(this.element[0], c.currentItem || c.element)) {
                this.options.activeClass && this.element.removeClass(this.options.activeClass);
                this.options.hoverClass && this.element.removeClass(this.options.hoverClass);
                this._trigger("drop", a, this.ui(c));
                return this.element
            }
            return false
        },
        ui: function (a) {
            return {
                draggable: a.currentItem || a.element,
                helper: a.helper,
                position: a.position,
                offset: a.positionAbs
            }
        }
    });
    d.extend(d.ui.droppable, {
        version: "1.8.8"
    });
    d.ui.intersect = function (a, b, c) {
        if (!b.offset) return false;
        var e = (a.positionAbs || a.position.absolute).left,
            g = e + a.helperProportions.width,
            f = (a.positionAbs || a.position.absolute).top,
            h = f + a.helperProportions.height,
            i = b.offset.left,
            k = i + b.proportions.width,
            j = b.offset.top,
            l = j + b.proportions.height;
        switch (c) {
        case "fit":
            return i <= e && g <= k && j <= f && h <= l;
        case "intersect":
            return i < e + a.helperProportions.width / 2 && g - a.helperProportions.width / 2 < k && j < f + a.helperProportions.height / 2 && h - a.helperProportions.height / 2 < l;
        case "pointer":
            return d.ui.isOver((a.positionAbs || a.position.absolute).top + (a.clickOffset || a.offset.click).top, (a.positionAbs || a.position.absolute).left + (a.clickOffset || a.offset.click).left, j, i, b.proportions.height, b.proportions.width);
        case "touch":
            return (f >= j && f <= l || h >= j && h <= l || f < j && h > l) && (e >= i && e <= k || g >= i && g <= k || e < i && g > k);
        default:
            return false
        }
    };
    d.ui.ddmanager = {
        current: null,
        droppables: {
            "default": []
        },
        prepareOffsets: function (a, b) {
            var c = d.ui.ddmanager.droppables[a.options.scope] || [],
                e = b ? b.type : null,
                g = (a.currentItem || a.element).find(":data(droppable)").andSelf(),
                f = 0;
            a: for (; f < c.length; f++) if (!(c[f].options.disabled || a && !c[f].accept.call(c[f].element[0], a.currentItem || a.element))) {
                for (var h = 0; h < g.length; h++) if (g[h] == c[f].element[0]) {
                    c[f].proportions.height = 0;
                    continue a
                }
                c[f].visible = c[f].element.css("display") != "none";
                if (c[f].visible) {
                    c[f].offset = c[f].element.offset();
                    c[f].proportions = {
                        width: c[f].element[0].offsetWidth,
                        height: c[f].element[0].offsetHeight
                    };
                    e == "mousedown" && c[f]._activate.call(c[f], b)
                }
            }
        },
        drop: function (a, b) {
            var c = false;
            d.each(d.ui.ddmanager.droppables[a.options.scope] || [], function () {
                if (this.options) {
                    if (!this.options.disabled && this.visible && d.ui.intersect(a, this, this.options.tolerance)) c = c || this._drop.call(this, b);
                    if (!this.options.disabled && this.visible && this.accept.call(this.element[0], a.currentItem || a.element)) {
                        this.isout = 1;
                        this.isover = 0;
                        this._deactivate.call(this, b)
                    }
                }
            });
            return c
        },
        drag: function (a, b) {
            a.options.refreshPositions && d.ui.ddmanager.prepareOffsets(a, b);
            d.each(d.ui.ddmanager.droppables[a.options.scope] || [], function () {
                if (!(this.options.disabled || this.greedyChild || !this.visible)) {
                    var c = d.ui.intersect(a, this, this.options.tolerance);
                    if (c = !c && this.isover == 1 ? "isout" : c && this.isover == 0 ? "isover" : null) {
                        var e;
                        if (this.options.greedy) {
                            var g = this.element.parents(":data(droppable):eq(0)");
                            if (g.length) {
                                e = d.data(g[0], "droppable");
                                e.greedyChild = c == "isover" ? 1 : 0
                            }
                        }
                        if (e && c == "isover") {
                            e.isover = 0;
                            e.isout = 1;
                            e._out.call(e, b)
                        }
                        this[c] = 1;
                        this[c == "isout" ? "isover" : "isout"] = 0;
                        this[c == "isover" ? "_over" : "_out"].call(this, b);
                        if (e && c == "isout") {
                            e.isout = 0;
                            e.isover = 1;
                            e._over.call(e, b)
                        }
                    }
                }
            })
        }
    }
})(jQuery);;
(function (e) {
    e.widget("ui.resizable", e.ui.mouse, {
        widgetEventPrefix: "resize",
        options: {
            alsoResize: false,
            animate: false,
            animateDuration: "slow",
            animateEasing: "swing",
            aspectRatio: false,
            autoHide: false,
            containment: false,
            ghost: false,
            grid: false,
            handles: "e,s,se",
            helper: false,
            maxHeight: null,
            maxWidth: null,
            minHeight: 10,
            minWidth: 10,
            zIndex: 1E3
        },
        _create: function () {
            var b = this,
                a = this.options;
            this.element.addClass("ui-resizable");
            e.extend(this, {
                _aspectRatio: !! a.aspectRatio,
                aspectRatio: a.aspectRatio,
                originalElement: this.element,
                _proportionallyResizeElements: [],
                _helper: a.helper || a.ghost || a.animate ? a.helper || "ui-resizable-helper" : null
            });
            if (this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) {
                /relative/.test(this.element.css("position")) && e.browser.opera && this.element.css({
                    position: "relative",
                    top: "auto",
                    left: "auto"
                });
                this.element.wrap(e('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({
                    position: this.element.css("position"),
                    width: this.element.outerWidth(),
                    height: this.element.outerHeight(),
                    top: this.element.css("top"),
                    left: this.element.css("left")
                }));
                this.element = this.element.parent().data("resizable", this.element.data("resizable"));
                this.elementIsWrapper = true;
                this.element.css({
                    marginLeft: this.originalElement.css("marginLeft"),
                    marginTop: this.originalElement.css("marginTop"),
                    marginRight: this.originalElement.css("marginRight"),
                    marginBottom: this.originalElement.css("marginBottom")
                });
                this.originalElement.css({
                    marginLeft: 0,
                    marginTop: 0,
                    marginRight: 0,
                    marginBottom: 0
                });
                this.originalResizeStyle = this.originalElement.css("resize");
                this.originalElement.css("resize", "none");
                this._proportionallyResizeElements.push(this.originalElement.css({
                    position: "static",
                    zoom: 1,
                    display: "block"
                }));
                this.originalElement.css({
                    margin: this.originalElement.css("margin")
                });
                this._proportionallyResize()
            }
            this.handles = a.handles || (!e(".ui-resizable-handle", this.element).length ? "e,s,se" : {
                n: ".ui-resizable-n",
                e: ".ui-resizable-e",
                s: ".ui-resizable-s",
                w: ".ui-resizable-w",
                se: ".ui-resizable-se",
                sw: ".ui-resizable-sw",
                ne: ".ui-resizable-ne",
                nw: ".ui-resizable-nw"
            });
            if (this.handles.constructor == String) {
                if (this.handles == "all") this.handles = "n,e,s,w,se,sw,ne,nw";
                var c = this.handles.split(",");
                this.handles = {};
                for (var d = 0; d < c.length; d++) {
                    var f = e.trim(c[d]),
                        g = e('<div class="ui-resizable-handle ' + ("ui-resizable-" + f) + '"></div>');
                    /sw|se|ne|nw/.test(f) && g.css({
                        zIndex: ++a.zIndex
                    });
                    "se" == f && g.addClass("ui-icon ui-icon-gripsmall-diagonal-se");
                    this.handles[f] = ".ui-resizable-" + f;
                    this.element.append(g)
                }
            }
            this._renderAxis = function (h) {
                h = h || this.element;
                for (var i in this.handles) {
                    if (this.handles[i].constructor == String) this.handles[i] = e(this.handles[i], this.element).show();
                    if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) {
                        var j = e(this.handles[i], this.element),
                            k = 0;
                        k = /sw|ne|nw|se|n|s/.test(i) ? j.outerHeight() : j.outerWidth();
                        j = ["padding", /ne|nw|n/.test(i) ? "Top" : /se|sw|s/.test(i) ? "Bottom" : /^e$/.test(i) ? "Right" : "Left"].join("");
                        h.css(j, k);
                        this._proportionallyResize()
                    }
                    e(this.handles[i])
                }
            };
            this._renderAxis(this.element);
            this._handles = e(".ui-resizable-handle", this.element).disableSelection();
            this._handles.mouseover(function () {
                if (!b.resizing) {
                    if (this.className) var h = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
                    b.axis = h && h[1] ? h[1] : "se"
                }
            });
            if (a.autoHide) {
                this._handles.hide();
                e(this.element).addClass("ui-resizable-autohide").hover(function () {
                    e(this).removeClass("ui-resizable-autohide");
                    b._handles.show()
                }, function () {
                    if (!b.resizing) {
                        e(this).addClass("ui-resizable-autohide");
                        b._handles.hide()
                    }
                })
            }
            this._mouseInit()
        },
        destroy: function () {
            this._mouseDestroy();
            var b = function (c) {
                    e(c).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()
                };
            if (this.elementIsWrapper) {
                b(this.element);
                var a = this.element;
                a.after(this.originalElement.css({
                    position: a.css("position"),
                    width: a.outerWidth(),
                    height: a.outerHeight(),
                    top: a.css("top"),
                    left: a.css("left")
                })).remove()
            }
            this.originalElement.css("resize", this.originalResizeStyle);
            b(this.originalElement);
            return this
        },
        _mouseCapture: function (b) {
            var a = false;
            for (var c in this.handles) if (e(this.handles[c])[0] == b.target) a = true;
            return !this.options.disabled && a
        },
        _mouseStart: function (b) {
            var a = this.options,
                c = this.element.position(),
                d = this.element;
            this.resizing = true;
            this.documentScroll = {
                top: e(document).scrollTop(),
                left: e(document).scrollLeft()
            };
            if (d.is(".ui-draggable") || /absolute/.test(d.css("position"))) d.css({
                position: "absolute",
                top: c.top,
                left: c.left
            });
            e.browser.opera && /relative/.test(d.css("position")) && d.css({
                position: "relative",
                top: "auto",
                left: "auto"
            });
            this._renderProxy();
            c = m(this.helper.css("left"));
            var f = m(this.helper.css("top"));
            if (a.containment) {
                c += e(a.containment).scrollLeft() || 0;
                f += e(a.containment).scrollTop() || 0
            }
            this.offset = this.helper.offset();
            this.position = {
                left: c,
                top: f
            };
            this.size = this._helper ? {
                width: d.outerWidth(),
                height: d.outerHeight()
            } : {
                width: d.width(),
                height: d.height()
            };
            this.originalSize = this._helper ? {
                width: d.outerWidth(),
                height: d.outerHeight()
            } : {
                width: d.width(),
                height: d.height()
            };
            this.originalPosition = {
                left: c,
                top: f
            };
            this.sizeDiff = {
                width: d.outerWidth() - d.width(),
                height: d.outerHeight() - d.height()
            };
            this.originalMousePosition = {
                left: b.pageX,
                top: b.pageY
            };
            this.aspectRatio = typeof a.aspectRatio == "number" ? a.aspectRatio : this.originalSize.width / this.originalSize.height || 1;
            a = e(".ui-resizable-" + this.axis).css("cursor");
            e("body").css("cursor", a == "auto" ? this.axis + "-resize" : a);
            d.addClass("ui-resizable-resizing");
            this._propagate("start", b);
            return true
        },
        _mouseDrag: function (b) {
            var a = this.helper,
                c = this.originalMousePosition,
                d = this._change[this.axis];
            if (!d) return false;
            c = d.apply(this, [b, b.pageX - c.left || 0, b.pageY - c.top || 0]);
            if (this._aspectRatio || b.shiftKey) c = this._updateRatio(c, b);
            c = this._respectSize(c, b);
            this._propagate("resize", b);
            a.css({
                top: this.position.top + "px",
                left: this.position.left + "px",
                width: this.size.width + "px",
                height: this.size.height + "px"
            });
            !this._helper && this._proportionallyResizeElements.length && this._proportionallyResize();
            this._updateCache(c);
            this._trigger("resize", b, this.ui());
            return false
        },
        _mouseStop: function (b) {
            this.resizing = false;
            var a = this.options,
                c = this;
            if (this._helper) {
                var d = this._proportionallyResizeElements,
                    f = d.length && /textarea/i.test(d[0].nodeName);
                d = f && e.ui.hasScroll(d[0], "left") ? 0 : c.sizeDiff.height;
                f = {
                    width: c.size.width - (f ? 0 : c.sizeDiff.width),
                    height: c.size.height - d
                };
                d = parseInt(c.element.css("left"), 10) + (c.position.left - c.originalPosition.left) || null;
                var g = parseInt(c.element.css("top"), 10) + (c.position.top - c.originalPosition.top) || null;
                a.animate || this.element.css(e.extend(f, {
                    top: g,
                    left: d
                }));
                c.helper.height(c.size.height);
                c.helper.width(c.size.width);
                this._helper && !a.animate && this._proportionallyResize()
            }
            e("body").css("cursor", "auto");
            this.element.removeClass("ui-resizable-resizing");
            this._propagate("stop", b);
            this._helper && this.helper.remove();
            return false
        },
        _updateCache: function (b) {
            this.offset = this.helper.offset();
            if (l(b.left)) this.position.left = b.left;
            if (l(b.top)) this.position.top = b.top;
            if (l(b.height)) this.size.height = b.height;
            if (l(b.width)) this.size.width = b.width
        },
        _updateRatio: function (b) {
            var a = this.position,
                c = this.size,
                d = this.axis;
            if (b.height) b.width = c.height * this.aspectRatio;
            else if (b.width) b.height = c.width / this.aspectRatio;
            if (d == "sw") {
                b.left = a.left + (c.width - b.width);
                b.top = null
            }
            if (d == "nw") {
                b.top = a.top + (c.height - b.height);
                b.left = a.left + (c.width - b.width)
            }
            return b
        },
        _respectSize: function (b) {
            var a = this.options,
                c = this.axis,
                d = l(b.width) && a.maxWidth && a.maxWidth < b.width,
                f = l(b.height) && a.maxHeight && a.maxHeight < b.height,
                g = l(b.width) && a.minWidth && a.minWidth > b.width,
                h = l(b.height) && a.minHeight && a.minHeight > b.height;
            if (g) b.width = a.minWidth;
            if (h) b.height = a.minHeight;
            if (d) b.width = a.maxWidth;
            if (f) b.height = a.maxHeight;
            var i = this.originalPosition.left + this.originalSize.width,
                j = this.position.top + this.size.height,
                k = /sw|nw|w/.test(c);
            c = /nw|ne|n/.test(c);
            if (g && k) b.left = i - a.minWidth;
            if (d && k) b.left = i - a.maxWidth;
            if (h && c) b.top = j - a.minHeight;
            if (f && c) b.top = j - a.maxHeight;
            if ((a = !b.width && !b.height) && !b.left && b.top) b.top = null;
            else if (a && !b.top && b.left) b.left = null;
            return b
        },
        _proportionallyResize: function () {
            if (this._proportionallyResizeElements.length) for (var b = this.helper || this.element, a = 0; a < this._proportionallyResizeElements.length; a++) {
                var c = this._proportionallyResizeElements[a];
                if (!this.borderDif) {
                    var d = [c.css("borderTopWidth"), c.css("borderRightWidth"), c.css("borderBottomWidth"), c.css("borderLeftWidth")],
                        f = [c.css("paddingTop"), c.css("paddingRight"), c.css("paddingBottom"), c.css("paddingLeft")];
                    this.borderDif = e.map(d, function (g, h) {
                        g = parseInt(g, 10) || 0;
                        h = parseInt(f[h], 10) || 0;
                        return g + h
                    })
                }
                e.browser.msie && (e(b).is(":hidden") || e(b).parents(":hidden").length) || c.css({
                    height: b.height() - this.borderDif[0] - this.borderDif[2] || 0,
                    width: b.width() - this.borderDif[1] - this.borderDif[3] || 0
                })
            }
        },
        _renderProxy: function () {
            var b = this.options;
            this.elementOffset = this.element.offset();
            if (this._helper) {
                this.helper = this.helper || e('<div style="overflow:hidden;"></div>');
                var a = e.browser.msie && e.browser.version < 7,
                    c = a ? 1 : 0;
                a = a ? 2 : -1;
                this.helper.addClass(this._helper).css({
                    width: this.element.outerWidth() + a,
                    height: this.element.outerHeight() + a,
                    position: "absolute",
                    left: this.elementOffset.left - c + "px",
                    top: this.elementOffset.top - c + "px",
                    zIndex: ++b.zIndex
                });
                this.helper.appendTo("body").disableSelection()
            } else this.helper = this.element
        },
        _change: {
            e: function (b, a) {
                return {
                    width: this.originalSize.width + a
                }
            },
            w: function (b, a) {
                return {
                    left: this.originalPosition.left + a,
                    width: this.originalSize.width - a
                }
            },
            n: function (b, a, c) {
                return {
                    top: this.originalPosition.top + c,
                    height: this.originalSize.height - c
                }
            },
            s: function (b, a, c) {
                return {
                    height: this.originalSize.height + c
                }
            },
            se: function (b, a, c) {
                return e.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [b, a, c]))
            },
            sw: function (b, a, c) {
                return e.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [b, a, c]))
            },
            ne: function (b, a, c) {
                return e.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [b, a, c]))
            },
            nw: function (b, a, c) {
                return e.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [b, a, c]))
            }
        },
        _propagate: function (b, a) {
            e.ui.plugin.call(this, b, [a, this.ui()]);
            b != "resize" && this._trigger(b, a, this.ui())
        },
        plugins: {},
        ui: function () {
            return {
                originalElement: this.originalElement,
                element: this.element,
                helper: this.helper,
                position: this.position,
                size: this.size,
                originalSize: this.originalSize,
                originalPosition: this.originalPosition
            }
        }
    });
    e.extend(e.ui.resizable, {
        version: "1.8.8"
    });
    e.ui.plugin.add("resizable", "alsoResize", {
        start: function () {
            var b = e(this).data("resizable").options,
                a = function (c) {
                    e(c).each(function () {
                        var d = e(this);
                        d.data("resizable-alsoresize", {
                            width: parseInt(d.width(), 10),
                            height: parseInt(d.height(), 10),
                            left: parseInt(d.css("left"), 10),
                            top: parseInt(d.css("top"), 10),
                            position: d.css("position")
                        })
                    })
                };
            if (typeof b.alsoResize == "object" && !b.alsoResize.parentNode) if (b.alsoResize.length) {
                b.alsoResize = b.alsoResize[0];
                a(b.alsoResize)
            } else e.each(b.alsoResize, function (c) {
                a(c)
            });
            else a(b.alsoResize)
        },
        resize: function (b, a) {
            var c = e(this).data("resizable");
            b = c.options;
            var d = c.originalSize,
                f = c.originalPosition,
                g = {
                    height: c.size.height - d.height || 0,
                    width: c.size.width - d.width || 0,
                    top: c.position.top - f.top || 0,
                    left: c.position.left - f.left || 0
                },
                h = function (i, j) {
                    e(i).each(function () {
                        var k = e(this),
                            q = e(this).data("resizable-alsoresize"),
                            p = {},
                            r = j && j.length ? j : k.parents(a.originalElement[0]).length ? ["width", "height"] : ["width", "height", "top", "left"];
                        e.each(r, function (n, o) {
                            if ((n = (q[o] || 0) + (g[o] || 0)) && n >= 0) p[o] = n || null
                        });
                        if (e.browser.opera && /relative/.test(k.css("position"))) {
                            c._revertToRelativePosition = true;
                            k.css({
                                position: "absolute",
                                top: "auto",
                                left: "auto"
                            })
                        }
                        k.css(p)
                    })
                };
            typeof b.alsoResize == "object" && !b.alsoResize.nodeType ? e.each(b.alsoResize, function (i, j) {
                h(i, j)
            }) : h(b.alsoResize)
        },
        stop: function () {
            var b = e(this).data("resizable"),
                a = b.options,
                c = function (d) {
                    e(d).each(function () {
                        var f = e(this);
                        f.css({
                            position: f.data("resizable-alsoresize").position
                        })
                    })
                };
            if (b._revertToRelativePosition) {
                b._revertToRelativePosition = false;
                typeof a.alsoResize == "object" && !a.alsoResize.nodeType ? e.each(a.alsoResize, function (d) {
                    c(d)
                }) : c(a.alsoResize)
            }
            e(this).removeData("resizable-alsoresize")
        }
    });
    e.ui.plugin.add("resizable", "animate", {
        stop: function (b) {
            var a = e(this).data("resizable"),
                c = a.options,
                d = a._proportionallyResizeElements,
                f = d.length && /textarea/i.test(d[0].nodeName),
                g = f && e.ui.hasScroll(d[0], "left") ? 0 : a.sizeDiff.height;
            f = {
                width: a.size.width - (f ? 0 : a.sizeDiff.width),
                height: a.size.height - g
            };
            g = parseInt(a.element.css("left"), 10) + (a.position.left - a.originalPosition.left) || null;
            var h = parseInt(a.element.css("top"), 10) + (a.position.top - a.originalPosition.top) || null;
            a.element.animate(e.extend(f, h && g ? {
                top: h,
                left: g
            } : {}), {
                duration: c.animateDuration,
                easing: c.animateEasing,
                step: function () {
                    var i = {
                        width: parseInt(a.element.css("width"), 10),
                        height: parseInt(a.element.css("height"), 10),
                        top: parseInt(a.element.css("top"), 10),
                        left: parseInt(a.element.css("left"), 10)
                    };
                    d && d.length && e(d[0]).css({
                        width: i.width,
                        height: i.height
                    });
                    a._updateCache(i);
                    a._propagate("resize", b)
                }
            })
        }
    });
    e.ui.plugin.add("resizable", "containment", {
        start: function () {
            var b = e(this).data("resizable"),
                a = b.element,
                c = b.options.containment;
            if (a = c instanceof e ? c.get(0) : /parent/.test(c) ? a.parent().get(0) : c) {
                b.containerElement = e(a);
                if (/document/.test(c) || c == document) {
                    b.containerOffset = {
                        left: 0,
                        top: 0
                    };
                    b.containerPosition = {
                        left: 0,
                        top: 0
                    };
                    b.parentData = {
                        element: e(document),
                        left: 0,
                        top: 0,
                        width: e(document).width(),
                        height: e(document).height() || document.body.parentNode.scrollHeight
                    }
                } else {
                    var d = e(a),
                        f = [];
                    e(["Top", "Right", "Left", "Bottom"]).each(function (i, j) {
                        f[i] = m(d.css("padding" + j))
                    });
                    b.containerOffset = d.offset();
                    b.containerPosition = d.position();
                    b.containerSize = {
                        height: d.innerHeight() - f[3],
                        width: d.innerWidth() - f[1]
                    };
                    c = b.containerOffset;
                    var g = b.containerSize.height,
                        h = b.containerSize.width;
                    h = e.ui.hasScroll(a, "left") ? a.scrollWidth : h;
                    g = e.ui.hasScroll(a) ? a.scrollHeight : g;
                    b.parentData = {
                        element: a,
                        left: c.left,
                        top: c.top,
                        width: h,
                        height: g
                    }
                }
            }
        },
        resize: function (b) {
            var a = e(this).data("resizable"),
                c = a.options,
                d = a.containerOffset,
                f = a.position;
            b = a._aspectRatio || b.shiftKey;
            var g = {
                top: 0,
                left: 0
            },
                h = a.containerElement;
            if (h[0] != document && /static/.test(h.css("position"))) g = d;
            if (f.left < (a._helper ? d.left : 0)) {
                a.size.width += a._helper ? a.position.left - d.left : a.position.left - g.left;
                if (b) a.size.height = a.size.width / c.aspectRatio;
                a.position.left = c.helper ? d.left : 0
            }
            if (f.top < (a._helper ? d.top : 0)) {
                a.size.height += a._helper ? a.position.top - d.top : a.position.top;
                if (b) a.size.width = a.size.height * c.aspectRatio;
                a.position.top = a._helper ? d.top : 0
            }
            a.offset.left = a.parentData.left + a.position.left;
            a.offset.top = a.parentData.top + a.position.top;
            c = Math.abs((a._helper ? a.offset.left - g.left : a.offset.left - g.left) + a.sizeDiff.width);
            d = Math.abs((a._helper ? a.offset.top - g.top : a.offset.top - d.top) + a.sizeDiff.height);
            f = a.containerElement.get(0) == a.element.parent().get(0);
            g = /relative|absolute/.test(a.containerElement.css("position"));
            if (f && g) c -= a.parentData.left;
            if (c + a.size.width >= a.parentData.width) {
                a.size.width = a.parentData.width - c;
                if (b) a.size.height = a.size.width / a.aspectRatio
            }
            if (d + a.size.height >= a.parentData.height) {
                a.size.height = a.parentData.height - d;
                if (b) a.size.width = a.size.height * a.aspectRatio
            }
        },
        stop: function () {
            var b = e(this).data("resizable"),
                a = b.options,
                c = b.containerOffset,
                d = b.containerPosition,
                f = b.containerElement,
                g = e(b.helper),
                h = g.offset(),
                i = g.outerWidth() - b.sizeDiff.width;
            g = g.outerHeight() - b.sizeDiff.height;
            b._helper && !a.animate && /relative/.test(f.css("position")) && e(this).css({
                left: h.left - d.left - c.left,
                width: i,
                height: g
            });
            b._helper && !a.animate && /static/.test(f.css("position")) && e(this).css({
                left: h.left - d.left - c.left,
                width: i,
                height: g
            })
        }
    });
    e.ui.plugin.add("resizable", "ghost", {
        start: function () {
            var b = e(this).data("resizable"),
                a = b.options,
                c = b.size;
            b.ghost = b.originalElement.clone();
            b.ghost.css({
                opacity: 0.25,
                display: "block",
                position: "relative",
                height: c.height,
                width: c.width,
                margin: 0,
                left: 0,
                top: 0
            }).addClass("ui-resizable-ghost").addClass(typeof a.ghost == "string" ? a.ghost : "");
            b.ghost.appendTo(b.helper)
        },
        resize: function () {
            var b = e(this).data("resizable");
            b.ghost && b.ghost.css({
                position: "relative",
                height: b.size.height,
                width: b.size.width
            })
        },
        stop: function () {
            var b = e(this).data("resizable");
            b.ghost && b.helper && b.helper.get(0).removeChild(b.ghost.get(0))
        }
    });
    e.ui.plugin.add("resizable", "grid", {
        resize: function () {
            var b = e(this).data("resizable"),
                a = b.options,
                c = b.size,
                d = b.originalSize,
                f = b.originalPosition,
                g = b.axis;
            a.grid = typeof a.grid == "number" ? [a.grid, a.grid] : a.grid;
            var h = Math.round((c.width - d.width) / (a.grid[0] || 1)) * (a.grid[0] || 1);
            a = Math.round((c.height - d.height) / (a.grid[1] || 1)) * (a.grid[1] || 1);
            if (/^(se|s|e)$/.test(g)) {
                b.size.width = d.width + h;
                b.size.height = d.height + a
            } else if (/^(ne)$/.test(g)) {
                b.size.width = d.width + h;
                b.size.height = d.height + a;
                b.position.top = f.top - a
            } else {
                if (/^(sw)$/.test(g)) {
                    b.size.width = d.width + h;
                    b.size.height = d.height + a
                } else {
                    b.size.width = d.width + h;
                    b.size.height = d.height + a;
                    b.position.top = f.top - a
                }
                b.position.left = f.left - h
            }
        }
    });
    var m = function (b) {
            return parseInt(b, 10) || 0
        },
        l = function (b) {
            return !isNaN(parseInt(b, 10))
        }
})(jQuery);;
(function (e) {
    e.widget("ui.selectable", e.ui.mouse, {
        options: {
            appendTo: "body",
            autoRefresh: true,
            distance: 0,
            filter: "*",
            tolerance: "touch"
        },
        _create: function () {
            var c = this;
            this.element.addClass("ui-selectable");
            this.dragged = false;
            var f;
            this.refresh = function () {
                f = e(c.options.filter, c.element[0]);
                f.each(function () {
                    var d = e(this),
                        b = d.offset();
                    e.data(this, "selectable-item", {
                        element: this,
                        $element: d,
                        left: b.left,
                        top: b.top,
                        right: b.left + d.outerWidth(),
                        bottom: b.top + d.outerHeight(),
                        startselected: false,
                        selected: d.hasClass("ui-selected"),
                        selecting: d.hasClass("ui-selecting"),
                        unselecting: d.hasClass("ui-unselecting")
                    })
                })
            };
            this.refresh();
            this.selectees = f.addClass("ui-selectee");
            this._mouseInit();
            this.helper = e("<div class='ui-selectable-helper'></div>")
        },
        destroy: function () {
            this.selectees.removeClass("ui-selectee").removeData("selectable-item");
            this.element.removeClass("ui-selectable ui-selectable-disabled").removeData("selectable").unbind(".selectable");
            this._mouseDestroy();
            return this
        },
        _mouseStart: function (c) {
            var f = this;
            this.opos = [c.pageX, c.pageY];
            if (!this.options.disabled) {
                var d = this.options;
                this.selectees = e(d.filter, this.element[0]);
                this._trigger("start", c);
                e(d.appendTo).append(this.helper);
                this.helper.css({
                    left: c.clientX,
                    top: c.clientY,
                    width: 0,
                    height: 0
                });
                d.autoRefresh && this.refresh();
                this.selectees.filter(".ui-selected").each(function () {
                    var b = e.data(this, "selectable-item");
                    b.startselected = true;
                    if (!c.metaKey) {
                        b.$element.removeClass("ui-selected");
                        b.selected = false;
                        b.$element.addClass("ui-unselecting");
                        b.unselecting = true;
                        f._trigger("unselecting", c, {
                            unselecting: b.element
                        })
                    }
                });
                e(c.target).parents().andSelf().each(function () {
                    var b = e.data(this, "selectable-item");
                    if (b) {
                        var g = !c.metaKey || !b.$element.hasClass("ui-selected");
                        b.$element.removeClass(g ? "ui-unselecting" : "ui-selected").addClass(g ? "ui-selecting" : "ui-unselecting");
                        b.unselecting = !g;
                        b.selecting = g;
                        (b.selected = g) ? f._trigger("selecting", c, {
                            selecting: b.element
                        }) : f._trigger("unselecting", c, {
                            unselecting: b.element
                        });
                        return false
                    }
                })
            }
        },
        _mouseDrag: function (c) {
            var f = this;
            this.dragged = true;
            if (!this.options.disabled) {
                var d = this.options,
                    b = this.opos[0],
                    g = this.opos[1],
                    h = c.pageX,
                    i = c.pageY;
                if (b > h) {
                    var j = h;
                    h = b;
                    b = j
                }
                if (g > i) {
                    j = i;
                    i = g;
                    g = j
                }
                this.helper.css({
                    left: b,
                    top: g,
                    width: h - b,
                    height: i - g
                });
                this.selectees.each(function () {
                    var a = e.data(this, "selectable-item");
                    if (!(!a || a.element == f.element[0])) {
                        var k = false;
                        if (d.tolerance == "touch") k = !(a.left > h || a.right < b || a.top > i || a.bottom < g);
                        else if (d.tolerance == "fit") k = a.left > b && a.right < h && a.top > g && a.bottom < i;
                        if (k) {
                            if (a.selected) {
                                a.$element.removeClass("ui-selected");
                                a.selected = false
                            }
                            if (a.unselecting) {
                                a.$element.removeClass("ui-unselecting");
                                a.unselecting = false
                            }
                            if (!a.selecting) {
                                a.$element.addClass("ui-selecting");
                                a.selecting = true;
                                f._trigger("selecting", c, {
                                    selecting: a.element
                                })
                            }
                        } else {
                            if (a.selecting) if (c.metaKey && a.startselected) {
                                a.$element.removeClass("ui-selecting");
                                a.selecting = false;
                                a.$element.addClass("ui-selected");
                                a.selected = true
                            } else {
                                a.$element.removeClass("ui-selecting");
                                a.selecting = false;
                                if (a.startselected) {
                                    a.$element.addClass("ui-unselecting");
                                    a.unselecting = true
                                }
                                f._trigger("unselecting", c, {
                                    unselecting: a.element
                                })
                            }
                            if (a.selected) if (!c.metaKey && !a.startselected) {
                                a.$element.removeClass("ui-selected");
                                a.selected = false;
                                a.$element.addClass("ui-unselecting");
                                a.unselecting = true;
                                f._trigger("unselecting", c, {
                                    unselecting: a.element
                                })
                            }
                        }
                    }
                });
                return false
            }
        },
        _mouseStop: function (c) {
            var f = this;
            this.dragged = false;
            e(".ui-unselecting", this.element[0]).each(function () {
                var d = e.data(this, "selectable-item");
                d.$element.removeClass("ui-unselecting");
                d.unselecting = false;
                d.startselected = false;
                f._trigger("unselected", c, {
                    unselected: d.element
                })
            });
            e(".ui-selecting", this.element[0]).each(function () {
                var d = e.data(this, "selectable-item");
                d.$element.removeClass("ui-selecting").addClass("ui-selected");
                d.selecting = false;
                d.selected = true;
                d.startselected = true;
                f._trigger("selected", c, {
                    selected: d.element
                })
            });
            this._trigger("stop", c);
            this.helper.remove();
            return false
        }
    });
    e.extend(e.ui.selectable, {
        version: "1.8.8"
    })
})(jQuery);;
(function (d) {
    d.widget("ui.sortable", d.ui.mouse, {
        widgetEventPrefix: "sort",
        options: {
            appendTo: "parent",
            axis: false,
            connectWith: false,
            containment: false,
            cursor: "auto",
            cursorAt: false,
            dropOnEmpty: true,
            forcePlaceholderSize: false,
            forceHelperSize: false,
            grid: false,
            handle: false,
            helper: "original",
            items: "> *",
            opacity: false,
            placeholder: false,
            revert: false,
            scroll: true,
            scrollSensitivity: 20,
            scrollSpeed: 20,
            scope: "default",
            tolerance: "intersect",
            zIndex: 1E3
        },
        _create: function () {
            this.containerCache = {};
            this.element.addClass("ui-sortable");
            this.refresh();
            this.floating = this.items.length ? /left|right/.test(this.items[0].item.css("float")) : false;
            this.offset = this.element.offset();
            this._mouseInit()
        },
        destroy: function () {
            this.element.removeClass("ui-sortable ui-sortable-disabled").removeData("sortable").unbind(".sortable");
            this._mouseDestroy();
            for (var a = this.items.length - 1; a >= 0; a--) this.items[a].item.removeData("sortable-item");
            return this
        },
        _setOption: function (a, b) {
            if (a === "disabled") {
                this.options[a] = b;
                this.widget()[b ? "addClass" : "removeClass"]("ui-sortable-disabled")
            } else d.Widget.prototype._setOption.apply(this, arguments)
        },
        _mouseCapture: function (a, b) {
            if (this.reverting) return false;
            if (this.options.disabled || this.options.type == "static") return false;
            this._refreshItems(a);
            var c = null,
                e = this;
            d(a.target).parents().each(function () {
                if (d.data(this, "sortable-item") == e) {
                    c = d(this);
                    return false
                }
            });
            if (d.data(a.target, "sortable-item") == e) c = d(a.target);
            if (!c) return false;
            if (this.options.handle && !b) {
                var f = false;
                d(this.options.handle, c).find("*").andSelf().each(function () {
                    if (this == a.target) f = true
                });
                if (!f) return false
            }
            this.currentItem = c;
            this._removeCurrentsFromItems();
            return true
        },
        _mouseStart: function (a, b, c) {
            b = this.options;
            var e = this;
            this.currentContainer = this;
            this.refreshPositions();
            this.helper = this._createHelper(a);
            this._cacheHelperProportions();
            this._cacheMargins();
            this.scrollParent = this.helper.scrollParent();
            this.offset = this.currentItem.offset();
            this.offset = {
                top: this.offset.top - this.margins.top,
                left: this.offset.left - this.margins.left
            };
            this.helper.css("position", "absolute");
            this.cssPosition = this.helper.css("position");
            d.extend(this.offset, {
                click: {
                    left: a.pageX - this.offset.left,
                    top: a.pageY - this.offset.top
                },
                parent: this._getParentOffset(),
                relative: this._getRelativeOffset()
            });
            this.originalPosition = this._generatePosition(a);
            this.originalPageX = a.pageX;
            this.originalPageY = a.pageY;
            b.cursorAt && this._adjustOffsetFromHelper(b.cursorAt);
            this.domPosition = {
                prev: this.currentItem.prev()[0],
                parent: this.currentItem.parent()[0]
            };
            this.helper[0] != this.currentItem[0] && this.currentItem.hide();
            this._createPlaceholder();
            b.containment && this._setContainment();
            if (b.cursor) {
                if (d("body").css("cursor")) this._storedCursor = d("body").css("cursor");
                d("body").css("cursor", b.cursor)
            }
            if (b.opacity) {
                if (this.helper.css("opacity")) this._storedOpacity = this.helper.css("opacity");
                this.helper.css("opacity", b.opacity)
            }
            if (b.zIndex) {
                if (this.helper.css("zIndex")) this._storedZIndex = this.helper.css("zIndex");
                this.helper.css("zIndex", b.zIndex)
            }
            if (this.scrollParent[0] != document && this.scrollParent[0].tagName != "HTML") this.overflowOffset = this.scrollParent.offset();
            this._trigger("start", a, this._uiHash());
            this._preserveHelperProportions || this._cacheHelperProportions();
            if (!c) for (c = this.containers.length - 1; c >= 0; c--) this.containers[c]._trigger("activate", a, e._uiHash(this));
            if (d.ui.ddmanager) d.ui.ddmanager.current = this;
            d.ui.ddmanager && !b.dropBehaviour && d.ui.ddmanager.prepareOffsets(this, a);
            this.dragging = true;
            this.helper.addClass("ui-sortable-helper");
            this._mouseDrag(a);
            return true
        },
        _mouseDrag: function (a) {
            this.position = this._generatePosition(a);
            this.positionAbs = this._convertPositionTo("absolute");
            if (!this.lastPositionAbs) this.lastPositionAbs = this.positionAbs;
            if (this.options.scroll) {
                var b = this.options,
                    c = false;
                if (this.scrollParent[0] != document && this.scrollParent[0].tagName != "HTML") {
                    if (this.overflowOffset.top + this.scrollParent[0].offsetHeight - a.pageY < b.scrollSensitivity) this.scrollParent[0].scrollTop = c = this.scrollParent[0].scrollTop + b.scrollSpeed;
                    else if (a.pageY - this.overflowOffset.top < b.scrollSensitivity) this.scrollParent[0].scrollTop = c = this.scrollParent[0].scrollTop - b.scrollSpeed;
                    if (this.overflowOffset.left + this.scrollParent[0].offsetWidth - a.pageX < b.scrollSensitivity) this.scrollParent[0].scrollLeft = c = this.scrollParent[0].scrollLeft + b.scrollSpeed;
                    else if (a.pageX - this.overflowOffset.left < b.scrollSensitivity) this.scrollParent[0].scrollLeft = c = this.scrollParent[0].scrollLeft - b.scrollSpeed
                } else {
                    if (a.pageY - d(document).scrollTop() < b.scrollSensitivity) c = d(document).scrollTop(d(document).scrollTop() - b.scrollSpeed);
                    else if (d(window).height() - (a.pageY - d(document).scrollTop()) < b.scrollSensitivity) c = d(document).scrollTop(d(document).scrollTop() + b.scrollSpeed);
                    if (a.pageX - d(document).scrollLeft() < b.scrollSensitivity) c = d(document).scrollLeft(d(document).scrollLeft() - b.scrollSpeed);
                    else if (d(window).width() - (a.pageX - d(document).scrollLeft()) < b.scrollSensitivity) c = d(document).scrollLeft(d(document).scrollLeft() + b.scrollSpeed)
                }
                c !== false && d.ui.ddmanager && !b.dropBehaviour && d.ui.ddmanager.prepareOffsets(this, a)
            }
            this.positionAbs = this._convertPositionTo("absolute");
            if (!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left + "px";
            if (!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top + "px";
            for (b = this.items.length - 1; b >= 0; b--) {
                c = this.items[b];
                var e = c.item[0],
                    f = this._intersectsWithPointer(c);
                if (f) if (e != this.currentItem[0] && this.placeholder[f == 1 ? "next" : "prev"]()[0] != e && !d.ui.contains(this.placeholder[0], e) && (this.options.type == "semi-dynamic" ? !d.ui.contains(this.element[0], e) : true)) {
                    this.direction = f == 1 ? "down" : "up";
                    if (this.options.tolerance == "pointer" || this._intersectsWithSides(c)) this._rearrange(a, c);
                    else break;
                    this._trigger("change", a, this._uiHash());
                    break
                }
            }
            this._contactContainers(a);
            d.ui.ddmanager && d.ui.ddmanager.drag(this, a);
            this._trigger("sort", a, this._uiHash());
            this.lastPositionAbs = this.positionAbs;
            return false
        },
        _mouseStop: function (a, b) {
            if (a) {
                d.ui.ddmanager && !this.options.dropBehaviour && d.ui.ddmanager.drop(this, a);
                if (this.options.revert) {
                    var c = this;
                    b = c.placeholder.offset();
                    c.reverting = true;
                    d(this.helper).animate({
                        left: b.left - this.offset.parent.left - c.margins.left + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollLeft),
                        top: b.top - this.offset.parent.top - c.margins.top + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollTop)
                    }, parseInt(this.options.revert, 10) || 500, function () {
                        c._clear(a)
                    })
                } else this._clear(a, b);
                return false
            }
        },
        cancel: function () {
            var a = this;
            if (this.dragging) {
                this._mouseUp();
                this.options.helper == "original" ? this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper") : this.currentItem.show();
                for (var b = this.containers.length - 1; b >= 0; b--) {
                    this.containers[b]._trigger("deactivate", null, a._uiHash(this));
                    if (this.containers[b].containerCache.over) {
                        this.containers[b]._trigger("out", null, a._uiHash(this));
                        this.containers[b].containerCache.over = 0
                    }
                }
            }
            this.placeholder[0].parentNode && this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
            this.options.helper != "original" && this.helper && this.helper[0].parentNode && this.helper.remove();
            d.extend(this, {
                helper: null,
                dragging: false,
                reverting: false,
                _noFinalSort: null
            });
            this.domPosition.prev ? d(this.domPosition.prev).after(this.currentItem) : d(this.domPosition.parent).prepend(this.currentItem);
            return this
        },
        serialize: function (a) {
            var b = this._getItemsAsjQuery(a && a.connected),
                c = [];
            a = a || {};
            d(b).each(function () {
                var e = (d(a.item || this).attr(a.attribute || "id") || "").match(a.expression || /(.+)[-=_](.+)/);
                if (e) c.push((a.key || e[1] + "[]") + "=" + (a.key && a.expression ? e[1] : e[2]))
            });
            !c.length && a.key && c.push(a.key + "=");
            return c.join("&")
        },
        toArray: function (a) {
            var b = this._getItemsAsjQuery(a && a.connected),
                c = [];
            a = a || {};
            b.each(function () {
                c.push(d(a.item || this).attr(a.attribute || "id") || "")
            });
            return c
        },
        _intersectsWith: function (a) {
            var b = this.positionAbs.left,
                c = b + this.helperProportions.width,
                e = this.positionAbs.top,
                f = e + this.helperProportions.height,
                g = a.left,
                h = g + a.width,
                i = a.top,
                k = i + a.height,
                j = this.offset.click.top,
                l = this.offset.click.left;
            j = e + j > i && e + j < k && b + l > g && b + l < h;
            return this.options.tolerance == "pointer" || this.options.forcePointerForContainers || this.options.tolerance != "pointer" && this.helperProportions[this.floating ? "width" : "height"] > a[this.floating ? "width" : "height"] ? j : g < b + this.helperProportions.width / 2 && c - this.helperProportions.width / 2 < h && i < e + this.helperProportions.height / 2 && f - this.helperProportions.height / 2 < k
        },
        _intersectsWithPointer: function (a) {
            var b = d.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, a.top, a.height);
            a = d.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, a.left, a.width);
            b = b && a;
            a = this._getDragVerticalDirection();
            var c = this._getDragHorizontalDirection();
            if (!b) return false;
            return this.floating ? c && c == "right" || a == "down" ? 2 : 1 : a && (a == "down" ? 2 : 1)
        },
        _intersectsWithSides: function (a) {
            var b = d.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, a.top + a.height / 2, a.height);
            a = d.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, a.left + a.width / 2, a.width);
            var c = this._getDragVerticalDirection(),
                e = this._getDragHorizontalDirection();
            return this.floating && e ? e == "right" && a || e == "left" && !a : c && (c == "down" && b || c == "up" && !b)
        },
        _getDragVerticalDirection: function () {
            var a = this.positionAbs.top - this.lastPositionAbs.top;
            return a != 0 && (a > 0 ? "down" : "up")
        },
        _getDragHorizontalDirection: function () {
            var a = this.positionAbs.left - this.lastPositionAbs.left;
            return a != 0 && (a > 0 ? "right" : "left")
        },
        refresh: function (a) {
            this._refreshItems(a);
            this.refreshPositions();
            return this
        },
        _connectWith: function () {
            var a = this.options;
            return a.connectWith.constructor == String ? [a.connectWith] : a.connectWith
        },
        _getItemsAsjQuery: function (a) {
            var b = [],
                c = [],
                e = this._connectWith();
            if (e && a) for (a = e.length - 1; a >= 0; a--) for (var f = d(e[a]), g = f.length - 1; g >= 0; g--) {
                var h = d.data(f[g], "sortable");
                if (h && h != this && !h.options.disabled) c.push([d.isFunction(h.options.items) ? h.options.items.call(h.element) : d(h.options.items, h.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), h])
            }
            c.push([d.isFunction(this.options.items) ? this.options.items.call(this.element, null, {
                options: this.options,
                item: this.currentItem
            }) : d(this.options.items, this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), this]);
            for (a = c.length - 1; a >= 0; a--) c[a][0].each(function () {
                b.push(this)
            });
            return d(b)
        },
        _removeCurrentsFromItems: function () {
            for (var a = this.currentItem.find(":data(sortable-item)"), b = 0; b < this.items.length; b++) for (var c = 0; c < a.length; c++) a[c] == this.items[b].item[0] && this.items.splice(b, 1)
        },
        _refreshItems: function (a) {
            this.items = [];
            this.containers = [this];
            var b = this.items,
                c = [
                    [d.isFunction(this.options.items) ? this.options.items.call(this.element[0], a, {
                        item: this.currentItem
                    }) : d(this.options.items, this.element), this]
                ],
                e = this._connectWith();
            if (e) for (var f = e.length - 1; f >= 0; f--) for (var g = d(e[f]), h = g.length - 1; h >= 0; h--) {
                var i = d.data(g[h], "sortable");
                if (i && i != this && !i.options.disabled) {
                    c.push([d.isFunction(i.options.items) ? i.options.items.call(i.element[0], a, {
                        item: this.currentItem
                    }) : d(i.options.items, i.element), i]);
                    this.containers.push(i)
                }
            }
            for (f = c.length - 1; f >= 0; f--) {
                a = c[f][1];
                e = c[f][0];
                h = 0;
                for (g = e.length; h < g; h++) {
                    i = d(e[h]);
                    i.data("sortable-item", a);
                    b.push({
                        item: i,
                        instance: a,
                        width: 0,
                        height: 0,
                        left: 0,
                        top: 0
                    })
                }
            }
        },
        refreshPositions: function (a) {
            if (this.offsetParent && this.helper) this.offset.parent = this._getParentOffset();
            for (var b = this.items.length - 1; b >= 0; b--) {
                var c = this.items[b],
                    e = this.options.toleranceElement ? d(this.options.toleranceElement, c.item) : c.item;
                if (!a) {
                    c.width = e.outerWidth();
                    c.height = e.outerHeight()
                }
                e = e.offset();
                c.left = e.left;
                c.top = e.top
            }
            if (this.options.custom && this.options.custom.refreshContainers) this.options.custom.refreshContainers.call(this);
            else for (b = this.containers.length - 1; b >= 0; b--) {
                e = this.containers[b].element.offset();
                this.containers[b].containerCache.left = e.left;
                this.containers[b].containerCache.top = e.top;
                this.containers[b].containerCache.width = this.containers[b].element.outerWidth();
                this.containers[b].containerCache.height = this.containers[b].element.outerHeight()
            }
            return this
        },
        _createPlaceholder: function (a) {
            var b = a || this,
                c = b.options;
            if (!c.placeholder || c.placeholder.constructor == String) {
                var e = c.placeholder;
                c.placeholder = {
                    element: function () {
                        var f = d(document.createElement(b.currentItem[0].nodeName)).addClass(e || b.currentItem[0].className + " ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];
                        if (!e) f.style.visibility = "hidden";
                        return f
                    },
                    update: function (f, g) {
                        if (!(e && !c.forcePlaceholderSize)) {
                            g.height() || g.height(b.currentItem.innerHeight() - parseInt(b.currentItem.css("paddingTop") || 0, 10) - parseInt(b.currentItem.css("paddingBottom") || 0, 10));
                            g.width() || g.width(b.currentItem.innerWidth() - parseInt(b.currentItem.css("paddingLeft") || 0, 10) - parseInt(b.currentItem.css("paddingRight") || 0, 10))
                        }
                    }
                }
            }
            b.placeholder = d(c.placeholder.element.call(b.element, b.currentItem));
            b.currentItem.after(b.placeholder);
            c.placeholder.update(b, b.placeholder)
        },
        _contactContainers: function (a) {
            for (var b = null, c = null, e = this.containers.length - 1; e >= 0; e--) if (!d.ui.contains(this.currentItem[0], this.containers[e].element[0])) if (this._intersectsWith(this.containers[e].containerCache)) {
                if (!(b && d.ui.contains(this.containers[e].element[0], b.element[0]))) {
                    b = this.containers[e];
                    c = e
                }
            } else if (this.containers[e].containerCache.over) {
                this.containers[e]._trigger("out", a, this._uiHash(this));
                this.containers[e].containerCache.over = 0
            }
            if (b) if (this.containers.length === 1) {
                this.containers[c]._trigger("over", a, this._uiHash(this));
                this.containers[c].containerCache.over = 1
            } else if (this.currentContainer != this.containers[c]) {
                b = 1E4;
                e = null;
                for (var f = this.positionAbs[this.containers[c].floating ? "left" : "top"], g = this.items.length - 1; g >= 0; g--) if (d.ui.contains(this.containers[c].element[0], this.items[g].item[0])) {
                    var h = this.items[g][this.containers[c].floating ? "left" : "top"];
                    if (Math.abs(h - f) < b) {
                        b = Math.abs(h - f);
                        e = this.items[g]
                    }
                }
                if (e || this.options.dropOnEmpty) {
                    this.currentContainer = this.containers[c];
                    e ? this._rearrange(a, e, null, true) : this._rearrange(a, null, this.containers[c].element, true);
                    this._trigger("change", a, this._uiHash());
                    this.containers[c]._trigger("change", a, this._uiHash(this));
                    this.options.placeholder.update(this.currentContainer, this.placeholder);
                    this.containers[c]._trigger("over", a, this._uiHash(this));
                    this.containers[c].containerCache.over = 1
                }
            }
        },
        _createHelper: function (a) {
            var b = this.options;
            a = d.isFunction(b.helper) ? d(b.helper.apply(this.element[0], [a, this.currentItem])) : b.helper == "clone" ? this.currentItem.clone() : this.currentItem;
            a.parents("body").length || d(b.appendTo != "parent" ? b.appendTo : this.currentItem[0].parentNode)[0].appendChild(a[0]);
            if (a[0] == this.currentItem[0]) this._storedCSS = {
                width: this.currentItem[0].style.width,
                height: this.currentItem[0].style.height,
                position: this.currentItem.css("position"),
                top: this.currentItem.css("top"),
                left: this.currentItem.css("left")
            };
            if (a[0].style.width == "" || b.forceHelperSize) a.width(this.currentItem.width());
            if (a[0].style.height == "" || b.forceHelperSize) a.height(this.currentItem.height());
            return a
        },
        _adjustOffsetFromHelper: function (a) {
            if (typeof a == "string") a = a.split(" ");
            if (d.isArray(a)) a = {
                left: +a[0],
                top: +a[1] || 0
            };
            if ("left" in a) this.offset.click.left = a.left + this.margins.left;
            if ("right" in a) this.offset.click.left = this.helperProportions.width - a.right + this.margins.left;
            if ("top" in a) this.offset.click.top = a.top + this.margins.top;
            if ("bottom" in a) this.offset.click.top = this.helperProportions.height - a.bottom + this.margins.top
        },
        _getParentOffset: function () {
            this.offsetParent = this.helper.offsetParent();
            var a = this.offsetParent.offset();
            if (this.cssPosition == "absolute" && this.scrollParent[0] != document && d.ui.contains(this.scrollParent[0], this.offsetParent[0])) {
                a.left += this.scrollParent.scrollLeft();
                a.top += this.scrollParent.scrollTop()
            }
            if (this.offsetParent[0] == document.body || this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == "html" && d.browser.msie) a = {
                top: 0,
                left: 0
            };
            return {
                top: a.top + (parseInt(this.offsetParent.css("borderTopWidth"), 10) || 0),
                left: a.left + (parseInt(this.offsetParent.css("borderLeftWidth"), 10) || 0)
            }
        },
        _getRelativeOffset: function () {
            if (this.cssPosition == "relative") {
                var a = this.currentItem.position();
                return {
                    top: a.top - (parseInt(this.helper.css("top"), 10) || 0) + this.scrollParent.scrollTop(),
                    left: a.left - (parseInt(this.helper.css("left"), 10) || 0) + this.scrollParent.scrollLeft()
                }
            } else return {
                top: 0,
                left: 0
            }
        },
        _cacheMargins: function () {
            this.margins = {
                left: parseInt(this.currentItem.css("marginLeft"), 10) || 0,
                top: parseInt(this.currentItem.css("marginTop"), 10) || 0
            }
        },
        _cacheHelperProportions: function () {
            this.helperProportions = {
                width: this.helper.outerWidth(),
                height: this.helper.outerHeight()
            }
        },
        _setContainment: function () {
            var a = this.options;
            if (a.containment == "parent") a.containment = this.helper[0].parentNode;
            if (a.containment == "document" || a.containment == "window") this.containment = [0 - this.offset.relative.left - this.offset.parent.left, 0 - this.offset.relative.top - this.offset.parent.top, d(a.containment == "document" ? document : window).width() - this.helperProportions.width - this.margins.left, (d(a.containment == "document" ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top];
            if (!/^(document|window|parent)$/.test(a.containment)) {
                var b = d(a.containment)[0];
                a = d(a.containment).offset();
                var c = d(b).css("overflow") != "hidden";
                this.containment = [a.left + (parseInt(d(b).css("borderLeftWidth"), 10) || 0) + (parseInt(d(b).css("paddingLeft"), 10) || 0) - this.margins.left, a.top + (parseInt(d(b).css("borderTopWidth"), 10) || 0) + (parseInt(d(b).css("paddingTop"), 10) || 0) - this.margins.top, a.left + (c ? Math.max(b.scrollWidth, b.offsetWidth) : b.offsetWidth) - (parseInt(d(b).css("borderLeftWidth"), 10) || 0) - (parseInt(d(b).css("paddingRight"), 10) || 0) - this.helperProportions.width - this.margins.left, a.top + (c ? Math.max(b.scrollHeight, b.offsetHeight) : b.offsetHeight) - (parseInt(d(b).css("borderTopWidth"), 10) || 0) - (parseInt(d(b).css("paddingBottom"), 10) || 0) - this.helperProportions.height - this.margins.top]
            }
        },
        _convertPositionTo: function (a, b) {
            if (!b) b = this.position;
            a = a == "absolute" ? 1 : -1;
            var c = this.cssPosition == "absolute" && !(this.scrollParent[0] != document && d.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent,
                e = /(html|body)/i.test(c[0].tagName);
            return {
                top: b.top + this.offset.relative.top * a + this.offset.parent.top * a - (d.browser.safari && this.cssPosition == "fixed" ? 0 : (this.cssPosition == "fixed" ? -this.scrollParent.scrollTop() : e ? 0 : c.scrollTop()) * a),
                left: b.left + this.offset.relative.left * a + this.offset.parent.left * a - (d.browser.safari && this.cssPosition == "fixed" ? 0 : (this.cssPosition == "fixed" ? -this.scrollParent.scrollLeft() : e ? 0 : c.scrollLeft()) * a)
            }
        },
        _generatePosition: function (a) {
            var b = this.options,
                c = this.cssPosition == "absolute" && !(this.scrollParent[0] != document && d.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent,
                e = /(html|body)/i.test(c[0].tagName);
            if (this.cssPosition == "relative" && !(this.scrollParent[0] != document && this.scrollParent[0] != this.offsetParent[0])) this.offset.relative = this._getRelativeOffset();
            var f = a.pageX,
                g = a.pageY;
            if (this.originalPosition) {
                if (this.containment) {
                    if (a.pageX - this.offset.click.left < this.containment[0]) f = this.containment[0] + this.offset.click.left;
                    if (a.pageY - this.offset.click.top < this.containment[1]) g = this.containment[1] + this.offset.click.top;
                    if (a.pageX - this.offset.click.left > this.containment[2]) f = this.containment[2] + this.offset.click.left;
                    if (a.pageY - this.offset.click.top > this.containment[3]) g = this.containment[3] + this.offset.click.top
                }
                if (b.grid) {
                    g = this.originalPageY + Math.round((g - this.originalPageY) / b.grid[1]) * b.grid[1];
                    g = this.containment ? !(g - this.offset.click.top < this.containment[1] || g - this.offset.click.top > this.containment[3]) ? g : !(g - this.offset.click.top < this.containment[1]) ? g - b.grid[1] : g + b.grid[1] : g;
                    f = this.originalPageX + Math.round((f - this.originalPageX) / b.grid[0]) * b.grid[0];
                    f = this.containment ? !(f - this.offset.click.left < this.containment[0] || f - this.offset.click.left > this.containment[2]) ? f : !(f - this.offset.click.left < this.containment[0]) ? f - b.grid[0] : f + b.grid[0] : f
                }
            }
            return {
                top: g - this.offset.click.top - this.offset.relative.top - this.offset.parent.top + (d.browser.safari && this.cssPosition == "fixed" ? 0 : this.cssPosition == "fixed" ? -this.scrollParent.scrollTop() : e ? 0 : c.scrollTop()),
                left: f - this.offset.click.left - this.offset.relative.left - this.offset.parent.left + (d.browser.safari && this.cssPosition == "fixed" ? 0 : this.cssPosition == "fixed" ? -this.scrollParent.scrollLeft() : e ? 0 : c.scrollLeft())
            }
        },
        _rearrange: function (a, b, c, e) {
            c ? c[0].appendChild(this.placeholder[0]) : b.item[0].parentNode.insertBefore(this.placeholder[0], this.direction == "down" ? b.item[0] : b.item[0].nextSibling);
            this.counter = this.counter ? ++this.counter : 1;
            var f = this,
                g = this.counter;
            window.setTimeout(function () {
                g == f.counter && f.refreshPositions(!e)
            }, 0)
        },
        _clear: function (a, b) {
            this.reverting = false;
            var c = [];
            !this._noFinalSort && this.currentItem[0].parentNode && this.placeholder.before(this.currentItem);
            this._noFinalSort = null;
            if (this.helper[0] == this.currentItem[0]) {
                for (var e in this._storedCSS) if (this._storedCSS[e] == "auto" || this._storedCSS[e] == "static") this._storedCSS[e] = "";
                this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")
            } else this.currentItem.show();
            this.fromOutside && !b && c.push(function (f) {
                this._trigger("receive", f, this._uiHash(this.fromOutside))
            });
            if ((this.fromOutside || this.domPosition.prev != this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent != this.currentItem.parent()[0]) && !b) c.push(function (f) {
                this._trigger("update", f, this._uiHash())
            });
            if (!d.ui.contains(this.element[0], this.currentItem[0])) {
                b || c.push(function (f) {
                    this._trigger("remove", f, this._uiHash())
                });
                for (e = this.containers.length - 1; e >= 0; e--) if (d.ui.contains(this.containers[e].element[0], this.currentItem[0]) && !b) {
                    c.push(function (f) {
                        return function (g) {
                            f._trigger("receive", g, this._uiHash(this))
                        }
                    }.call(this, this.containers[e]));
                    c.push(function (f) {
                        return function (g) {
                            f._trigger("update", g, this._uiHash(this))
                        }
                    }.call(this, this.containers[e]))
                }
            }
            for (e = this.containers.length - 1; e >= 0; e--) {
                b || c.push(function (f) {
                    return function (g) {
                        f._trigger("deactivate", g, this._uiHash(this))
                    }
                }.call(this, this.containers[e]));
                if (this.containers[e].containerCache.over) {
                    c.push(function (f) {
                        return function (g) {
                            f._trigger("out", g, this._uiHash(this))
                        }
                    }.call(this, this.containers[e]));
                    this.containers[e].containerCache.over = 0
                }
            }
            this._storedCursor && d("body").css("cursor", this._storedCursor);
            this._storedOpacity && this.helper.css("opacity", this._storedOpacity);
            if (this._storedZIndex) this.helper.css("zIndex", this._storedZIndex == "auto" ? "" : this._storedZIndex);
            this.dragging = false;
            if (this.cancelHelperRemoval) {
                if (!b) {
                    this._trigger("beforeStop", a, this._uiHash());
                    for (e = 0; e < c.length; e++) c[e].call(this, a);
                    this._trigger("stop", a, this._uiHash())
                }
                return false
            }
            b || this._trigger("beforeStop", a, this._uiHash());
            this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
            this.helper[0] != this.currentItem[0] && this.helper.remove();
            this.helper = null;
            if (!b) {
                for (e = 0; e < c.length; e++) c[e].call(this, a);
                this._trigger("stop", a, this._uiHash())
            }
            this.fromOutside = false;
            return true
        },
        _trigger: function () {
            d.Widget.prototype._trigger.apply(this, arguments) === false && this.cancel()
        },
        _uiHash: function (a) {
            var b = a || this;
            return {
                helper: b.helper,
                placeholder: b.placeholder || d([]),
                position: b.position,
                originalPosition: b.originalPosition,
                offset: b.positionAbs,
                item: b.currentItem,
                sender: a ? a.element : null
            }
        }
    });
    d.extend(d.ui.sortable, {
        version: "1.8.8"
    })
})(jQuery);;
(function (d) {
    d.widget("ui.autocomplete", {
        options: {
            appendTo: "body",
            delay: 300,
            minLength: 1,
            position: {
                my: "left top",
                at: "left bottom",
                collision: "none"
            },
            source: null
        },
        pending: 0,
        _create: function () {
            var a = this,
                b = this.element[0].ownerDocument,
                f;
            this.element.addClass("ui-autocomplete-input").attr("autocomplete", "off").attr({
                role: "textbox",
                "aria-autocomplete": "list",
                "aria-haspopup": "true"
            }).bind("keydown.autocomplete", function (c) {
                if (!(a.options.disabled || a.element.attr("readonly"))) {
                    f = false;
                    var e = d.ui.keyCode;
                    switch (c.keyCode) {
                    case e.PAGE_UP:
                        a._move("previousPage", c);
                        break;
                    case e.PAGE_DOWN:
                        a._move("nextPage", c);
                        break;
                    case e.UP:
                        a._move("previous", c);
                        c.preventDefault();
                        break;
                    case e.DOWN:
                        a._move("next", c);
                        c.preventDefault();
                        break;
                    case e.ENTER:
                    case e.NUMPAD_ENTER:
                        if (a.menu.active) {
                            f = true;
                            c.preventDefault()
                        }
                    case e.TAB:
                        if (!a.menu.active) return;
                        a.menu.select(c);
                        break;
                    case e.ESCAPE:
                        a.element.val(a.term);
                        a.close(c);
                        break;
                    default:
                        clearTimeout(a.searching);
                        a.searching = setTimeout(function () {
                            if (a.term != a.element.val()) {
                                a.selectedItem = null;
                                a.search(null, c)
                            }
                        }, a.options.delay);
                        break
                    }
                }
            }).bind("keypress.autocomplete", function (c) {
                if (f) {
                    f = false;
                    c.preventDefault()
                }
            }).bind("focus.autocomplete", function () {
                if (!a.options.disabled) {
                    a.selectedItem = null;
                    a.previous = a.element.val()
                }
            }).bind("blur.autocomplete", function (c) {
                if (!a.options.disabled) {
                    clearTimeout(a.searching);
                    a.closing = setTimeout(function () {
                        a.close(c);
                        a._change(c)
                    }, 150)
                }
            });
            this._initSource();
            this.response = function () {
                return a._response.apply(a, arguments)
            };
            this.menu = d("<ul></ul>").addClass("ui-autocomplete").appendTo(d(this.options.appendTo || "body", b)[0]).mousedown(function (c) {
                var e = a.menu.element[0];
                d(c.target).closest(".ui-menu-item").length || setTimeout(function () {
                    d(document).one("mousedown", function (g) {
                        g.target !== a.element[0] && g.target !== e && !d.ui.contains(e, g.target) && a.close()
                    })
                }, 1);
                setTimeout(function () {
                    clearTimeout(a.closing)
                }, 13)
            }).menu({
                focus: function (c, e) {
                    e = e.item.data("item.autocomplete");
                    false !== a._trigger("focus", c, {
                        item: e
                    }) && /^key/.test(c.originalEvent.type) && a.element.val(e.value)
                },
                selected: function (c, e) {
                    var g = e.item.data("item.autocomplete"),
                        h = a.previous;
                    if (a.element[0] !== b.activeElement) {
                        a.element.focus();
                        a.previous = h;
                        setTimeout(function () {
                            a.previous = h;
                            a.selectedItem = g
                        }, 1)
                    }
                    false !== a._trigger("select", c, {
                        item: g
                    }) && a.element.val(g.value);
                    a.term = a.element.val();
                    a.close(c);
                    a.selectedItem = g
                },
                blur: function () {
                    a.menu.element.is(":visible") && a.element.val() !== a.term && a.element.val(a.term)
                }
            }).zIndex(this.element.zIndex() + 1).css({
                top: 0,
                left: 0
            }).hide().data("menu");
            d.fn.bgiframe && this.menu.element.bgiframe()
        },
        destroy: function () {
            this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup");
            this.menu.element.remove();
            d.Widget.prototype.destroy.call(this)
        },
        _setOption: function (a, b) {
            d.Widget.prototype._setOption.apply(this, arguments);
            a === "source" && this._initSource();
            if (a === "appendTo") this.menu.element.appendTo(d(b || "body", this.element[0].ownerDocument)[0]);
            a === "disabled" && b && this.xhr && this.xhr.abort()
        },
        _initSource: function () {
            var a = this,
                b, f;
            if (d.isArray(this.options.source)) {
                b = this.options.source;
                this.source = function (c, e) {
                    e(d.ui.autocomplete.filter(b, c.term))
                }
            } else if (typeof this.options.source === "string") {
                f = this.options.source;
                this.source = function (c, e) {
                    a.xhr && a.xhr.abort();
                    a.xhr = d.ajax({
                        url: f,
                        data: c,
                        dataType: "json",
                        success: function (g, h, i) {
                            i === a.xhr && e(g);
                            a.xhr = null
                        },
                        error: function (g) {
                            g === a.xhr && e([]);
                            a.xhr = null
                        }
                    })
                }
            } else this.source = this.options.source
        },
        search: function (a, b) {
            a = a != null ? a : this.element.val();
            this.term = this.element.val();
            if (a.length < this.options.minLength) return this.close(b);
            clearTimeout(this.closing);
            if (this._trigger("search", b) !== false) return this._search(a)
        },
        _search: function (a) {
            this.pending++;
            this.element.addClass("ui-autocomplete-loading");
            this.source({
                term: a
            }, this.response)
        },
        _response: function (a) {
            if (!this.options.disabled && a && a.length) {
                a = this._normalize(a);
                this._suggest(a);
                this._trigger("open")
            } else this.close();
            this.pending--;
            this.pending || this.element.removeClass("ui-autocomplete-loading")
        },
        close: function (a) {
            clearTimeout(this.closing);
            if (this.menu.element.is(":visible")) {
                this.menu.element.hide();
                this.menu.deactivate();
                this._trigger("close", a)
            }
        },
        _change: function (a) {
            this.previous !== this.element.val() && this._trigger("change", a, {
                item: this.selectedItem
            })
        },
        _normalize: function (a) {
            if (a.length && a[0].label && a[0].value) return a;
            return d.map(a, function (b) {
                if (typeof b === "string") return {
                    label: b,
                    value: b
                };
                return d.extend({
                    label: b.label || b.value,
                    value: b.value || b.label
                }, b)
            })
        },
        _suggest: function (a) {
            var b = this.menu.element.empty().zIndex(this.element.zIndex() + 1);
            this._renderMenu(b, a);
            this.menu.deactivate();
            this.menu.refresh();
            b.show();
            this._resizeMenu();
            b.position(d.extend({
                of: this.element
            }, this.options.position))
        },
        _resizeMenu: function () {
            var a = this.menu.element;
            a.outerWidth(Math.max(a.width("").outerWidth(), this.element.outerWidth()))
        },
        _renderMenu: function (a, b) {
            var f = this;
            d.each(b, function (c, e) {
                f._renderItem(a, e)
            })
        },
        _renderItem: function (a, b) {
            return d("<li></li>").data("item.autocomplete", b).append(d("<a></a>").text(b.label)).appendTo(a)
        },
        _move: function (a, b) {
            if (this.menu.element.is(":visible")) if (this.menu.first() && /^previous/.test(a) || this.menu.last() && /^next/.test(a)) {
                this.element.val(this.term);
                this.menu.deactivate()
            } else this.menu[a](b);
            else this.search(null, b)
        },
        widget: function () {
            return this.menu.element
        }
    });
    d.extend(d.ui.autocomplete, {
        escapeRegex: function (a) {
            return a.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&")
        },
        filter: function (a, b) {
            var f = new RegExp(d.ui.autocomplete.escapeRegex(b), "i");
            return d.grep(a, function (c) {
                return f.test(c.label || c.value || c)
            })
        }
    })
})(jQuery);
(function (d) {
    d.widget("ui.menu", {
        _create: function () {
            var a = this;
            this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr({
                role: "listbox",
                "aria-activedescendant": "ui-active-menuitem"
            }).click(function (b) {
                if (d(b.target).closest(".ui-menu-item a").length) {
                    b.preventDefault();
                    a.select(b)
                }
            });
            this.refresh()
        },
        refresh: function () {
            var a = this;
            this.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role", "menuitem").children("a").addClass("ui-corner-all").attr("tabindex", -1).mouseenter(function (b) {
                a.activate(b, d(this).parent())
            }).mouseleave(function () {
                a.deactivate()
            })
        },
        activate: function (a, b) {
            this.deactivate();
            if (this.hasScroll()) {
                var f = b.offset().top - this.element.offset().top,
                    c = this.element.attr("scrollTop"),
                    e = this.element.height();
                if (f < 0) this.element.attr("scrollTop", c + f);
                else f >= e && this.element.attr("scrollTop", c + f - e + b.height())
            }
            this.active = b.eq(0).children("a").addClass("ui-state-hover").attr("id", "ui-active-menuitem").end();
            this._trigger("focus", a, {
                item: b
            })
        },
        deactivate: function () {
            if (this.active) {
                this.active.children("a").removeClass("ui-state-hover").removeAttr("id");
                this._trigger("blur");
                this.active = null
            }
        },
        next: function (a) {
            this.move("next", ".ui-menu-item:first", a)
        },
        previous: function (a) {
            this.move("prev", ".ui-menu-item:last", a)
        },
        first: function () {
            return this.active && !this.active.prevAll(".ui-menu-item").length
        },
        last: function () {
            return this.active && !this.active.nextAll(".ui-menu-item").length
        },
        move: function (a, b, f) {
            if (this.active) {
                a = this.active[a + "All"](".ui-menu-item").eq(0);
                a.length ? this.activate(f, a) : this.activate(f, this.element.children(b))
            } else this.activate(f, this.element.children(b))
        },
        nextPage: function (a) {
            if (this.hasScroll()) if (!this.active || this.last()) this.activate(a, this.element.children(".ui-menu-item:first"));
            else {
                var b = this.active.offset().top,
                    f = this.element.height(),
                    c = this.element.children(".ui-menu-item").filter(function () {
                        var e = d(this).offset().top - b - f + d(this).height();
                        return e < 10 && e > -10
                    });
                c.length || (c = this.element.children(".ui-menu-item:last"));
                this.activate(a, c)
            } else this.activate(a, this.element.children(".ui-menu-item").filter(!this.active || this.last() ? ":first" : ":last"))
        },
        previousPage: function (a) {
            if (this.hasScroll()) if (!this.active || this.first()) this.activate(a, this.element.children(".ui-menu-item:last"));
            else {
                var b = this.active.offset().top,
                    f = this.element.height();
                result = this.element.children(".ui-menu-item").filter(function () {
                    var c = d(this).offset().top - b + f - d(this).height();
                    return c < 10 && c > -10
                });
                result.length || (result = this.element.children(".ui-menu-item:first"));
                this.activate(a, result)
            } else this.activate(a, this.element.children(".ui-menu-item").filter(!this.active || this.first() ? ":last" : ":first"))
        },
        hasScroll: function () {
            return this.element.height() < this.element.attr("scrollHeight")
        },
        select: function (a) {
            this._trigger("selected", a, {
                item: this.active
            })
        }
    })
})(jQuery);;
(function (a) {
    var g, i = function (b) {
            a(":ui-button", b.target.form).each(function () {
                var c = a(this).data("button");
                setTimeout(function () {
                    c.refresh()
                }, 1)
            })
        },
        h = function (b) {
            var c = b.name,
                d = b.form,
                e = a([]);
            if (c) e = d ? a(d).find("[name='" + c + "']") : a("[name='" + c + "']", b.ownerDocument).filter(function () {
                return !this.form
            });
            return e
        };
    a.widget("ui.button", {
        options: {
            disabled: null,
            text: true,
            label: null,
            icons: {
                primary: null,
                secondary: null
            }
        },
        _create: function () {
            this.element.closest("form").unbind("reset.button").bind("reset.button", i);
            if (typeof this.options.disabled !== "boolean") this.options.disabled = this.element.attr("disabled");
            this._determineButtonType();
            this.hasTitle = !! this.buttonElement.attr("title");
            var b = this,
                c = this.options,
                d = this.type === "checkbox" || this.type === "radio",
                e = "ui-state-hover" + (!d ? " ui-state-active" : "");
            if (c.label === null) c.label = this.buttonElement.html();
            if (this.element.is(":disabled")) c.disabled = true;
            this.buttonElement.addClass("ui-button ui-widget ui-state-default ui-corner-all").attr("role", "button").bind("mouseenter.button", function () {
                if (!c.disabled) {
                    a(this).addClass("ui-state-hover");
                    this === g && a(this).addClass("ui-state-active")
                }
            }).bind("mouseleave.button", function () {
                c.disabled || a(this).removeClass(e)
            }).bind("focus.button", function () {
                a(this).addClass("ui-state-focus")
            }).bind("blur.button", function () {
                a(this).removeClass("ui-state-focus")
            });
            d && this.element.bind("change.button", function () {
                b.refresh()
            });
            if (this.type === "checkbox") this.buttonElement.bind("click.button", function () {
                if (c.disabled) return false;
                a(this).toggleClass("ui-state-active");
                b.buttonElement.attr("aria-pressed", b.element[0].checked)
            });
            else if (this.type === "radio") this.buttonElement.bind("click.button", function () {
                if (c.disabled) return false;
                a(this).addClass("ui-state-active");
                b.buttonElement.attr("aria-pressed", true);
                var f = b.element[0];
                h(f).not(f).map(function () {
                    return a(this).button("widget")[0]
                }).removeClass("ui-state-active").attr("aria-pressed", false)
            });
            else {
                this.buttonElement.bind("mousedown.button", function () {
                    if (c.disabled) return false;
                    a(this).addClass("ui-state-active");
                    g = this;
                    a(document).one("mouseup", function () {
                        g = null
                    })
                }).bind("mouseup.button", function () {
                    if (c.disabled) return false;
                    a(this).removeClass("ui-state-active")
                }).bind("keydown.button", function (f) {
                    if (c.disabled) return false;
                    if (f.keyCode == a.ui.keyCode.SPACE || f.keyCode == a.ui.keyCode.ENTER) a(this).addClass("ui-state-active")
                }).bind("keyup.button", function () {
                    a(this).removeClass("ui-state-active")
                });
                this.buttonElement.is("a") && this.buttonElement.keyup(function (f) {
                    f.keyCode === a.ui.keyCode.SPACE && a(this).click()
                })
            }
            this._setOption("disabled", c.disabled)
        },
        _determineButtonType: function () {
            this.type = this.element.is(":checkbox") ? "checkbox" : this.element.is(":radio") ? "radio" : this.element.is("input") ? "input" : "button";
            if (this.type === "checkbox" || this.type === "radio") {
                this.buttonElement = this.element.parents().last().find("label[for=" + this.element.attr("id") + "]");
                this.element.addClass("ui-helper-hidden-accessible");
                var b = this.element.is(":checked");
                b && this.buttonElement.addClass("ui-state-active");
                this.buttonElement.attr("aria-pressed", b)
            } else this.buttonElement = this.element
        },
        widget: function () {
            return this.buttonElement
        },
        destroy: function () {
            this.element.removeClass("ui-helper-hidden-accessible");
            this.buttonElement.removeClass("ui-button ui-widget ui-state-default ui-corner-all ui-state-hover ui-state-active  ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only").removeAttr("role").removeAttr("aria-pressed").html(this.buttonElement.find(".ui-button-text").html());
            this.hasTitle || this.buttonElement.removeAttr("title");
            a.Widget.prototype.destroy.call(this)
        },
        _setOption: function (b, c) {
            a.Widget.prototype._setOption.apply(this, arguments);
            if (b === "disabled") c ? this.element.attr("disabled", true) : this.element.removeAttr("disabled");
            this._resetButton()
        },
        refresh: function () {
            var b = this.element.is(":disabled");
            b !== this.options.disabled && this._setOption("disabled", b);
            if (this.type === "radio") h(this.element[0]).each(function () {
                a(this).is(":checked") ? a(this).button("widget").addClass("ui-state-active").attr("aria-pressed", true) : a(this).button("widget").removeClass("ui-state-active").attr("aria-pressed", false)
            });
            else if (this.type === "checkbox") this.element.is(":checked") ? this.buttonElement.addClass("ui-state-active").attr("aria-pressed", true) : this.buttonElement.removeClass("ui-state-active").attr("aria-pressed", false)
        },
        _resetButton: function () {
            if (this.type === "input") this.options.label && this.element.val(this.options.label);
            else {
                var b = this.buttonElement.removeClass("ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only"),
                    c = a("<span></span>").addClass("ui-button-text").html(this.options.label).appendTo(b.empty()).text(),
                    d = this.options.icons,
                    e = d.primary && d.secondary;
                if (d.primary || d.secondary) {
                    b.addClass("ui-button-text-icon" + (e ? "s" : d.primary ? "-primary" : "-secondary"));
                    d.primary && b.prepend("<span class='ui-button-icon-primary ui-icon " + d.primary + "'></span>");
                    d.secondary && b.append("<span class='ui-button-icon-secondary ui-icon " + d.secondary + "'></span>");
                    if (!this.options.text) {
                        b.addClass(e ? "ui-button-icons-only" : "ui-button-icon-only").removeClass("ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary");
                        this.hasTitle || b.attr("title", c)
                    }
                } else b.addClass("ui-button-text-only")
            }
        }
    });
    a.widget("ui.buttonset", {
        options: {
            items: ":button, :submit, :reset, :checkbox, :radio, a, :data(button)"
        },
        _create: function () {
            this.element.addClass("ui-buttonset")
        },
        _init: function () {
            this.refresh()
        },
        _setOption: function (b, c) {
            b === "disabled" && this.buttons.button("option", b, c);
            a.Widget.prototype._setOption.apply(this, arguments)
        },
        refresh: function () {
            this.buttons = this.element.find(this.options.items).filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function () {
                return a(this).button("widget")[0]
            }).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass("ui-corner-left").end().filter(":last").addClass("ui-corner-right").end().end()
        },
        destroy: function () {
            this.element.removeClass("ui-buttonset");
            this.buttons.map(function () {
                return a(this).button("widget")[0]
            }).removeClass("ui-corner-left ui-corner-right").end().button("destroy");
            a.Widget.prototype.destroy.call(this)
        }
    })
})(jQuery);;
(function (c, j) {
    var k = {
        buttons: true,
        height: true,
        maxHeight: true,
        maxWidth: true,
        minHeight: true,
        minWidth: true,
        width: true
    },
        l = {
            maxHeight: true,
            maxWidth: true,
            minHeight: true,
            minWidth: true
        };
    c.widget("ui.dialog", {
        options: {
            autoOpen: true,
            buttons: {},
            closeOnEscape: true,
            closeText: "close",
            dialogClass: "",
            draggable: true,
            hide: null,
            height: "auto",
            maxHeight: false,
            maxWidth: false,
            minHeight: 150,
            minWidth: 150,
            modal: false,
            position: {
                my: "center",
                at: "center",
                collision: "fit",
                using: function (a) {
                    var b = c(this).css(a).offset().top;
                    b < 0 && c(this).css("top", a.top - b)
                }
            },
            resizable: true,
            show: null,
            stack: true,
            title: "",
            width: 300,
            zIndex: 1E3
        },
        _create: function () {
            this.originalTitle = this.element.attr("title");
            if (typeof this.originalTitle !== "string") this.originalTitle = "";
            this.options.title = this.options.title || this.originalTitle;
            var a = this,
                b = a.options,
                d = b.title || "&#160;",
                e = c.ui.dialog.getTitleId(a.element),
                g = (a.uiDialog = c("<div></div>")).appendTo(document.body).hide().addClass("ui-dialog ui-widget ui-widget-content ui-corner-all " + b.dialogClass).css({
                    zIndex: b.zIndex
                }).attr("tabIndex", -1).css("outline", 0).keydown(function (i) {
                    if (b.closeOnEscape && i.keyCode && i.keyCode === c.ui.keyCode.ESCAPE) {
                        a.close(i);
                        i.preventDefault()
                    }
                }).attr({
                    role: "dialog",
                    "aria-labelledby": e
                }).mousedown(function (i) {
                    a.moveToTop(false, i)
                });
            a.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(g);
            var f = (a.uiDialogTitlebar = c("<div></div>")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(g),
                h = c('<a href="#"></a>').addClass("ui-dialog-titlebar-close ui-corner-all").attr("role", "button").hover(function () {
                    h.addClass("ui-state-hover")
                }, function () {
                    h.removeClass("ui-state-hover")
                }).focus(function () {
                    h.addClass("ui-state-focus")
                }).blur(function () {
                    h.removeClass("ui-state-focus")
                }).click(function (i) {
                    a.close(i);
                    return false
                }).appendTo(f);
            (a.uiDialogTitlebarCloseText = c("<span></span>")).addClass("ui-icon ui-icon-closethick").text(b.closeText).appendTo(h);
            c("<span></span>").addClass("ui-dialog-title").attr("id", e).html(d).prependTo(f);
            if (c.isFunction(b.beforeclose) && !c.isFunction(b.beforeClose)) b.beforeClose = b.beforeclose;
            f.find("*").add(f).disableSelection();
            b.draggable && c.fn.draggable && a._makeDraggable();
            b.resizable && c.fn.resizable && a._makeResizable();
            a._createButtons(b.buttons);
            a._isOpen = false;
            c.fn.bgiframe && g.bgiframe()
        },
        _init: function () {
            this.options.autoOpen && this.open()
        },
        destroy: function () {
            var a = this;
            a.overlay && a.overlay.destroy();
            a.uiDialog.hide();
            a.element.unbind(".dialog").removeData("dialog").removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body");
            a.uiDialog.remove();
            a.originalTitle && a.element.attr("title", a.originalTitle);
            return a
        },
        widget: function () {
            return this.uiDialog
        },
        close: function (a) {
            var b = this,
                d, e;
            if (false !== b._trigger("beforeClose", a)) {
                b.overlay && b.overlay.destroy();
                b.uiDialog.unbind("keypress.ui-dialog");
                b._isOpen = false;
                if (b.options.hide) b.uiDialog.hide(b.options.hide, function () {
                    b._trigger("close", a)
                });
                else {
                    b.uiDialog.hide();
                    b._trigger("close", a)
                }
                c.ui.dialog.overlay.resize();
                if (b.options.modal) {
                    d = 0;
                    c(".ui-dialog").each(function () {
                        if (this !== b.uiDialog[0]) {
                            e = c(this).css("z-index");
                            isNaN(e) || (d = Math.max(d, e))
                        }
                    });
                    c.ui.dialog.maxZ = d
                }
                return b
            }
        },
        isOpen: function () {
            return this._isOpen
        },
        moveToTop: function (a, b) {
            var d = this,
                e = d.options;
            if (e.modal && !a || !e.stack && !e.modal) return d._trigger("focus", b);
            if (e.zIndex > c.ui.dialog.maxZ) c.ui.dialog.maxZ = e.zIndex;
            if (d.overlay) {
                c.ui.dialog.maxZ += 1;
                d.overlay.$el.css("z-index", c.ui.dialog.overlay.maxZ = c.ui.dialog.maxZ)
            }
            a = {
                scrollTop: d.element.attr("scrollTop"),
                scrollLeft: d.element.attr("scrollLeft")
            };
            c.ui.dialog.maxZ += 1;
            d.uiDialog.css("z-index", c.ui.dialog.maxZ);
            d.element.attr(a);
            d._trigger("focus", b);
            return d
        },
        open: function () {
            if (!this._isOpen) {
                var a = this,
                    b = a.options,
                    d = a.uiDialog;
                a.overlay = b.modal ? new c.ui.dialog.overlay(a) : null;
                a._size();
                a._position(b.position);
                d.show(b.show);
                a.moveToTop(true);
                b.modal && d.bind("keypress.ui-dialog", function (e) {
                    if (e.keyCode === c.ui.keyCode.TAB) {
                        var g = c(":tabbable", this),
                            f = g.filter(":first");
                        g = g.filter(":last");
                        if (e.target === g[0] && !e.shiftKey) {
                            f.focus(1);
                            return false
                        } else if (e.target === f[0] && e.shiftKey) {
                            g.focus(1);
                            return false
                        }
                    }
                });
                c(a.element.find(":tabbable").get().concat(d.find(".ui-dialog-buttonpane :tabbable").get().concat(d.get()))).eq(0).focus();
                a._isOpen = true;
                a._trigger("open");
                return a
            }
        },
        _createButtons: function (a) {
            var b = this,
                d = false,
                e = c("<div></div>").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),
                g = c("<div></div>").addClass("ui-dialog-buttonset").appendTo(e);
            b.uiDialog.find(".ui-dialog-buttonpane").remove();
            typeof a === "object" && a !== null && c.each(a, function () {
                return !(d = true)
            });
            if (d) {
                c.each(a, function (f, h) {
                    h = c.isFunction(h) ? {
                        click: h,
                        text: f
                    } : h;
                    f = c('<button type="button"></button>').attr(h, true).unbind("click").click(function () {
                        h.click.apply(b.element[0], arguments)
                    }).appendTo(g);
                    c.fn.button && f.button()
                });
                e.appendTo(b.uiDialog)
            }
        },
        _makeDraggable: function () {
            function a(f) {
                return {
                    position: f.position,
                    offset: f.offset
                }
            }
            var b = this,
                d = b.options,
                e = c(document),
                g;
            b.uiDialog.draggable({
                cancel: ".ui-dialog-content, .ui-dialog-titlebar-close",
                handle: ".ui-dialog-titlebar",
                containment: "document",
                start: function (f, h) {
                    g = d.height === "auto" ? "auto" : c(this).height();
                    c(this).height(c(this).height()).addClass("ui-dialog-dragging");
                    b._trigger("dragStart", f, a(h))
                },
                drag: function (f, h) {
                    b._trigger("drag", f, a(h))
                },
                stop: function (f, h) {
                    d.position = [h.position.left - e.scrollLeft(), h.position.top - e.scrollTop()];
                    c(this).removeClass("ui-dialog-dragging").height(g);
                    b._trigger("dragStop", f, a(h));
                    c.ui.dialog.overlay.resize()
                }
            })
        },
        _makeResizable: function (a) {
            function b(f) {
                return {
                    originalPosition: f.originalPosition,
                    originalSize: f.originalSize,
                    position: f.position,
                    size: f.size
                }
            }
            a = a === j ? this.options.resizable : a;
            var d = this,
                e = d.options,
                g = d.uiDialog.css("position");
            a = typeof a === "string" ? a : "n,e,s,w,se,sw,ne,nw";
            d.uiDialog.resizable({
                cancel: ".ui-dialog-content",
                containment: "document",
                alsoResize: d.element,
                maxWidth: e.maxWidth,
                maxHeight: e.maxHeight,
                minWidth: e.minWidth,
                minHeight: d._minHeight(),
                handles: a,
                start: function (f, h) {
                    c(this).addClass("ui-dialog-resizing");
                    d._trigger("resizeStart", f, b(h))
                },
                resize: function (f, h) {
                    d._trigger("resize", f, b(h))
                },
                stop: function (f, h) {
                    c(this).removeClass("ui-dialog-resizing");
                    e.height = c(this).height();
                    e.width = c(this).width();
                    d._trigger("resizeStop", f, b(h));
                    c.ui.dialog.overlay.resize()
                }
            }).css("position", g).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")
        },
        _minHeight: function () {
            var a = this.options;
            return a.height === "auto" ? a.minHeight : Math.min(a.minHeight, a.height)
        },
        _position: function (a) {
            var b = [],
                d = [0, 0],
                e;
            if (a) {
                if (typeof a === "string" || typeof a === "object" && "0" in a) {
                    b = a.split ? a.split(" ") : [a[0], a[1]];
                    if (b.length === 1) b[1] = b[0];
                    c.each(["left", "top"], function (g, f) {
                        if (+b[g] === b[g]) {
                            d[g] = b[g];
                            b[g] = f
                        }
                    });
                    a = {
                        my: b.join(" "),
                        at: b.join(" "),
                        offset: d.join(" ")
                    }
                }
                a = c.extend({}, c.ui.dialog.prototype.options.position, a)
            } else a = c.ui.dialog.prototype.options.position;
            (e = this.uiDialog.is(":visible")) || this.uiDialog.show();
            this.uiDialog.css({
                top: 0,
                left: 0
            }).position(c.extend({
                of: window
            }, a));
            e || this.uiDialog.hide()
        },
        _setOptions: function (a) {
            var b = this,
                d = {},
                e = false;
            c.each(a, function (g, f) {
                b._setOption(g, f);
                if (g in k) e = true;
                if (g in l) d[g] = f
            });
            e && this._size();
            this.uiDialog.is(":data(resizable)") && this.uiDialog.resizable("option", d)
        },
        _setOption: function (a, b) {
            var d = this,
                e = d.uiDialog;
            switch (a) {
            case "beforeclose":
                a = "beforeClose";
                break;
            case "buttons":
                d._createButtons(b);
                break;
            case "closeText":
                d.uiDialogTitlebarCloseText.text("" + b);
                break;
            case "dialogClass":
                e.removeClass(d.options.dialogClass).addClass("ui-dialog ui-widget ui-widget-content ui-corner-all " + b);
                break;
            case "disabled":
                b ? e.addClass("ui-dialog-disabled") : e.removeClass("ui-dialog-disabled");
                break;
            case "draggable":
                var g = e.is(":data(draggable)");
                g && !b && e.draggable("destroy");
                !g && b && d._makeDraggable();
                break;
            case "position":
                d._position(b);
                break;
            case "resizable":
                (g = e.is(":data(resizable)")) && !b && e.resizable("destroy");
                g && typeof b === "string" && e.resizable("option", "handles", b);
                !g && b !== false && d._makeResizable(b);
                break;
            case "title":
                c(".ui-dialog-title", d.uiDialogTitlebar).html("" + (b || "&#160;"));
                break
            }
            c.Widget.prototype._setOption.apply(d, arguments)
        },
        _size: function () {
            var a = this.options,
                b, d, e = this.uiDialog.is(":visible");
            this.element.show().css({
                width: "auto",
                minHeight: 0,
                height: 0
            });
            if (a.minWidth > a.width) a.width = a.minWidth;
            b = this.uiDialog.css({
                height: "auto",
                width: a.width
            }).height();
            d = Math.max(0, a.minHeight - b);
            if (a.height === "auto") if (c.support.minHeight) this.element.css({
                minHeight: d,
                height: "auto"
            });
            else {
                this.uiDialog.show();
                a = this.element.css("height", "auto").height();
                e || this.uiDialog.hide();
                this.element.height(Math.max(a, d))
            } else this.element.height(Math.max(a.height - b, 0));
            this.uiDialog.is(":data(resizable)") && this.uiDialog.resizable("option", "minHeight", this._minHeight())
        }
    });
    c.extend(c.ui.dialog, {
        version: "1.8.8",
        uuid: 0,
        maxZ: 0,
        getTitleId: function (a) {
            a = a.attr("id");
            if (!a) {
                this.uuid += 1;
                a = this.uuid
            }
            return "ui-dialog-title-" + a
        },
        overlay: function (a) {
            this.$el = c.ui.dialog.overlay.create(a)
        }
    });
    c.extend(c.ui.dialog.overlay, {
        instances: [],
        oldInstances: [],
        maxZ: 0,
        events: c.map("focus,mousedown,mouseup,keydown,keypress,click".split(","), function (a) {
            return a + ".dialog-overlay"
        }).join(" "),
        create: function (a) {
            if (this.instances.length === 0) {
                setTimeout(function () {
                    c.ui.dialog.overlay.instances.length && c(document).bind(c.ui.dialog.overlay.events, function (d) {
                        if (c(d.target).zIndex() < c.ui.dialog.overlay.maxZ) return false
                    })
                }, 1);
                c(document).bind("keydown.dialog-overlay", function (d) {
                    if (a.options.closeOnEscape && d.keyCode && d.keyCode === c.ui.keyCode.ESCAPE) {
                        a.close(d);
                        d.preventDefault()
                    }
                });
                c(window).bind("resize.dialog-overlay", c.ui.dialog.overlay.resize)
            }
            var b = (this.oldInstances.pop() || c("<div></div>").addClass("ui-widget-overlay")).appendTo(document.body).css({
                width: this.width(),
                height: this.height()
            });
            c.fn.bgiframe && b.bgiframe();
            this.instances.push(b);
            return b
        },
        destroy: function (a) {
            var b = c.inArray(a, this.instances);
            b != -1 && this.oldInstances.push(this.instances.splice(b, 1)[0]);
            this.instances.length === 0 && c([document, window]).unbind(".dialog-overlay");
            a.remove();
            var d = 0;
            c.each(this.instances, function () {
                d = Math.max(d, this.css("z-index"))
            });
            this.maxZ = d
        },
        height: function () {
            var a, b;
            if (c.browser.msie && c.browser.version < 7) {
                a = Math.max(document.documentElement.scrollHeight, document.body.scrollHeight);
                b = Math.max(document.documentElement.offsetHeight, document.body.offsetHeight);
                return a < b ? c(window).height() + "px" : a + "px"
            } else return c(document).height() + "px"
        },
        width: function () {
            var a, b;
            if (c.browser.msie && c.browser.version < 7) {
                a = Math.max(document.documentElement.scrollWidth, document.body.scrollWidth);
                b = Math.max(document.documentElement.offsetWidth, document.body.offsetWidth);
                return a < b ? c(window).width() + "px" : a + "px"
            } else return c(document).width() + "px"
        },
        resize: function () {
            var a = c([]);
            c.each(c.ui.dialog.overlay.instances, function () {
                a = a.add(this)
            });
            a.css({
                width: 0,
                height: 0
            }).css({
                width: c.ui.dialog.overlay.width(),
                height: c.ui.dialog.overlay.height()
            })
        }
    });
    c.extend(c.ui.dialog.overlay.prototype, {
        destroy: function () {
            c.ui.dialog.overlay.destroy(this.$el)
        }
    })
})(jQuery);;
(function (d) {
    d.widget("ui.slider", d.ui.mouse, {
        widgetEventPrefix: "slide",
        options: {
            animate: false,
            distance: 0,
            max: 100,
            min: 0,
            orientation: "horizontal",
            range: false,
            step: 1,
            value: 0,
            values: null
        },
        _create: function () {
            var b = this,
                a = this.options;
            this._mouseSliding = this._keySliding = false;
            this._animateOff = true;
            this._handleIndex = null;
            this._detectOrientation();
            this._mouseInit();
            this.element.addClass("ui-slider ui-slider-" + this.orientation + " ui-widget ui-widget-content ui-corner-all");
            a.disabled && this.element.addClass("ui-slider-disabled ui-disabled");
            this.range = d([]);
            if (a.range) {
                if (a.range === true) {
                    this.range = d("<div></div>");
                    if (!a.values) a.values = [this._valueMin(), this._valueMin()];
                    if (a.values.length && a.values.length !== 2) a.values = [a.values[0], a.values[0]]
                } else this.range = d("<div></div>");
                this.range.appendTo(this.element).addClass("ui-slider-range");
                if (a.range === "min" || a.range === "max") this.range.addClass("ui-slider-range-" + a.range);
                this.range.addClass("ui-widget-header")
            }
            d(".ui-slider-handle", this.element).length === 0 && d("<a href='#'></a>").appendTo(this.element).addClass("ui-slider-handle");
            if (a.values && a.values.length) for (; d(".ui-slider-handle", this.element).length < a.values.length;) d("<a href='#'></a>").appendTo(this.element).addClass("ui-slider-handle");
            this.handles = d(".ui-slider-handle", this.element).addClass("ui-state-default ui-corner-all");
            this.handle = this.handles.eq(0);
            this.handles.add(this.range).filter("a").click(function (c) {
                c.preventDefault()
            }).hover(function () {
                a.disabled || d(this).addClass("ui-state-hover")
            }, function () {
                d(this).removeClass("ui-state-hover")
            }).focus(function () {
                if (a.disabled) d(this).blur();
                else {
                    d(".ui-slider .ui-state-focus").removeClass("ui-state-focus");
                    d(this).addClass("ui-state-focus")
                }
            }).blur(function () {
                d(this).removeClass("ui-state-focus")
            });
            this.handles.each(function (c) {
                d(this).data("index.ui-slider-handle", c)
            });
            this.handles.keydown(function (c) {
                var e = true,
                    f = d(this).data("index.ui-slider-handle"),
                    h, g, i;
                if (!b.options.disabled) {
                    switch (c.keyCode) {
                    case d.ui.keyCode.HOME:
                    case d.ui.keyCode.END:
                    case d.ui.keyCode.PAGE_UP:
                    case d.ui.keyCode.PAGE_DOWN:
                    case d.ui.keyCode.UP:
                    case d.ui.keyCode.RIGHT:
                    case d.ui.keyCode.DOWN:
                    case d.ui.keyCode.LEFT:
                        e = false;
                        if (!b._keySliding) {
                            b._keySliding = true;
                            d(this).addClass("ui-state-active");
                            h = b._start(c, f);
                            if (h === false) return
                        }
                        break
                    }
                    i = b.options.step;
                    h = b.options.values && b.options.values.length ? (g = b.values(f)) : (g = b.value());
                    switch (c.keyCode) {
                    case d.ui.keyCode.HOME:
                        g = b._valueMin();
                        break;
                    case d.ui.keyCode.END:
                        g = b._valueMax();
                        break;
                    case d.ui.keyCode.PAGE_UP:
                        g = b._trimAlignValue(h + (b._valueMax() - b._valueMin()) / 5);
                        break;
                    case d.ui.keyCode.PAGE_DOWN:
                        g = b._trimAlignValue(h - (b._valueMax() - b._valueMin()) / 5);
                        break;
                    case d.ui.keyCode.UP:
                    case d.ui.keyCode.RIGHT:
                        if (h === b._valueMax()) return;
                        g = b._trimAlignValue(h + i);
                        break;
                    case d.ui.keyCode.DOWN:
                    case d.ui.keyCode.LEFT:
                        if (h === b._valueMin()) return;
                        g = b._trimAlignValue(h - i);
                        break
                    }
                    b._slide(c, f, g);
                    return e
                }
            }).keyup(function (c) {
                var e = d(this).data("index.ui-slider-handle");
                if (b._keySliding) {
                    b._keySliding = false;
                    b._stop(c, e);
                    b._change(c, e);
                    d(this).removeClass("ui-state-active")
                }
            });
            this._refreshValue();
            this._animateOff = false
        },
        destroy: function () {
            this.handles.remove();
            this.range.remove();
            this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider");
            this._mouseDestroy();
            return this
        },
        _mouseCapture: function (b) {
            var a = this.options,
                c, e, f, h, g;
            if (a.disabled) return false;
            this.elementSize = {
                width: this.element.outerWidth(),
                height: this.element.outerHeight()
            };
            this.elementOffset = this.element.offset();
            c = this._normValueFromMouse({
                x: b.pageX,
                y: b.pageY
            });
            e = this._valueMax() - this._valueMin() + 1;
            h = this;
            this.handles.each(function (i) {
                var j = Math.abs(c - h.values(i));
                if (e > j) {
                    e = j;
                    f = d(this);
                    g = i
                }
            });
            if (a.range === true && this.values(1) === a.min) {
                g += 1;
                f = d(this.handles[g])
            }
            if (this._start(b, g) === false) return false;
            this._mouseSliding = true;
            h._handleIndex = g;
            f.addClass("ui-state-active").focus();
            a = f.offset();
            this._clickOffset = !d(b.target).parents().andSelf().is(".ui-slider-handle") ? {
                left: 0,
                top: 0
            } : {
                left: b.pageX - a.left - f.width() / 2,
                top: b.pageY - a.top - f.height() / 2 - (parseInt(f.css("borderTopWidth"), 10) || 0) - (parseInt(f.css("borderBottomWidth"), 10) || 0) + (parseInt(f.css("marginTop"), 10) || 0)
            };
            this.handles.hasClass("ui-state-hover") || this._slide(b, g, c);
            return this._animateOff = true
        },
        _mouseStart: function () {
            return true
        },
        _mouseDrag: function (b) {
            var a = this._normValueFromMouse({
                x: b.pageX,
                y: b.pageY
            });
            this._slide(b, this._handleIndex, a);
            return false
        },
        _mouseStop: function (b) {
            this.handles.removeClass("ui-state-active");
            this._mouseSliding = false;
            this._stop(b, this._handleIndex);
            this._change(b, this._handleIndex);
            this._clickOffset = this._handleIndex = null;
            return this._animateOff = false
        },
        _detectOrientation: function () {
            this.orientation = this.options.orientation === "vertical" ? "vertical" : "horizontal"
        },
        _normValueFromMouse: function (b) {
            var a;
            if (this.orientation === "horizontal") {
                a = this.elementSize.width;
                b = b.x - this.elementOffset.left - (this._clickOffset ? this._clickOffset.left : 0)
            } else {
                a = this.elementSize.height;
                b = b.y - this.elementOffset.top - (this._clickOffset ? this._clickOffset.top : 0)
            }
            a = b / a;
            if (a > 1) a = 1;
            if (a < 0) a = 0;
            if (this.orientation === "vertical") a = 1 - a;
            b = this._valueMax() - this._valueMin();
            return this._trimAlignValue(this._valueMin() + a * b)
        },
        _start: function (b, a) {
            var c = {
                handle: this.handles[a],
                value: this.value()
            };
            if (this.options.values && this.options.values.length) {
                c.value = this.values(a);
                c.values = this.values()
            }
            return this._trigger("start", b, c)
        },
        _slide: function (b, a, c) {
            var e;
            if (this.options.values && this.options.values.length) {
                e = this.values(a ? 0 : 1);
                if (this.options.values.length === 2 && this.options.range === true && (a === 0 && c > e || a === 1 && c < e)) c = e;
                if (c !== this.values(a)) {
                    e = this.values();
                    e[a] = c;
                    b = this._trigger("slide", b, {
                        handle: this.handles[a],
                        value: c,
                        values: e
                    });
                    this.values(a ? 0 : 1);
                    b !== false && this.values(a, c, true)
                }
            } else if (c !== this.value()) {
                b = this._trigger("slide", b, {
                    handle: this.handles[a],
                    value: c
                });
                b !== false && this.value(c)
            }
        },
        _stop: function (b, a) {
            var c = {
                handle: this.handles[a],
                value: this.value()
            };
            if (this.options.values && this.options.values.length) {
                c.value = this.values(a);
                c.values = this.values()
            }
            this._trigger("stop", b, c)
        },
        _change: function (b, a) {
            if (!this._keySliding && !this._mouseSliding) {
                var c = {
                    handle: this.handles[a],
                    value: this.value()
                };
                if (this.options.values && this.options.values.length) {
                    c.value = this.values(a);
                    c.values = this.values()
                }
                this._trigger("change", b, c)
            }
        },
        value: function (b) {
            if (arguments.length) {
                this.options.value = this._trimAlignValue(b);
                this._refreshValue();
                this._change(null, 0)
            }
            return this._value()
        },
        values: function (b, a) {
            var c, e, f;
            if (arguments.length > 1) {
                this.options.values[b] = this._trimAlignValue(a);
                this._refreshValue();
                this._change(null, b)
            }
            if (arguments.length) if (d.isArray(arguments[0])) {
                c = this.options.values;
                e = arguments[0];
                for (f = 0; f < c.length; f += 1) {
                    c[f] = this._trimAlignValue(e[f]);
                    this._change(null, f)
                }
                this._refreshValue()
            } else return this.options.values && this.options.values.length ? this._values(b) : this.value();
            else return this._values()
        },
        _setOption: function (b, a) {
            var c, e = 0;
            if (d.isArray(this.options.values)) e = this.options.values.length;
            d.Widget.prototype._setOption.apply(this, arguments);
            switch (b) {
            case "disabled":
                if (a) {
                    this.handles.filter(".ui-state-focus").blur();
                    this.handles.removeClass("ui-state-hover");
                    this.handles.attr("disabled", "disabled");
                    this.element.addClass("ui-disabled")
                } else {
                    this.handles.removeAttr("disabled");
                    this.element.removeClass("ui-disabled")
                }
                break;
            case "orientation":
                this._detectOrientation();
                this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-" + this.orientation);
                this._refreshValue();
                break;
            case "value":
                this._animateOff = true;
                this._refreshValue();
                this._change(null, 0);
                this._animateOff = false;
                break;
            case "values":
                this._animateOff = true;
                this._refreshValue();
                for (c = 0; c < e; c += 1) this._change(null, c);
                this._animateOff = false;
                break
            }
        },
        _value: function () {
            var b = this.options.value;
            return b = this._trimAlignValue(b)
        },
        _values: function (b) {
            var a, c;
            if (arguments.length) {
                a = this.options.values[b];
                return a = this._trimAlignValue(a)
            } else {
                a = this.options.values.slice();
                for (c = 0; c < a.length; c += 1) a[c] = this._trimAlignValue(a[c]);
                return a
            }
        },
        _trimAlignValue: function (b) {
            if (b <= this._valueMin()) return this._valueMin();
            if (b >= this._valueMax()) return this._valueMax();
            var a = this.options.step > 0 ? this.options.step : 1,
                c = (b - this._valueMin()) % a;
            alignValue = b - c;
            if (Math.abs(c) * 2 >= a) alignValue += c > 0 ? a : -a;
            return parseFloat(alignValue.toFixed(5))
        },
        _valueMin: function () {
            return this.options.min
        },
        _valueMax: function () {
            return this.options.max
        },
        _refreshValue: function () {
            var b = this.options.range,
                a = this.options,
                c = this,
                e = !this._animateOff ? a.animate : false,
                f, h = {},
                g, i, j, l;
            if (this.options.values && this.options.values.length) this.handles.each(function (k) {
                f = (c.values(k) - c._valueMin()) / (c._valueMax() - c._valueMin()) * 100;
                h[c.orientation === "horizontal" ? "left" : "bottom"] = f + "%";
                d(this).stop(1, 1)[e ? "animate" : "css"](h, a.animate);
                if (c.options.range === true) if (c.orientation === "horizontal") {
                    if (k === 0) c.range.stop(1, 1)[e ? "animate" : "css"]({
                        left: f + "%"
                    }, a.animate);
                    if (k === 1) c.range[e ? "animate" : "css"]({
                        width: f - g + "%"
                    }, {
                        queue: false,
                        duration: a.animate
                    })
                } else {
                    if (k === 0) c.range.stop(1, 1)[e ? "animate" : "css"]({
                        bottom: f + "%"
                    }, a.animate);
                    if (k === 1) c.range[e ? "animate" : "css"]({
                        height: f - g + "%"
                    }, {
                        queue: false,
                        duration: a.animate
                    })
                }
                g = f
            });
            else {
                i = this.value();
                j = this._valueMin();
                l = this._valueMax();
                f = l !== j ? (i - j) / (l - j) * 100 : 0;
                h[c.orientation === "horizontal" ? "left" : "bottom"] = f + "%";
                this.handle.stop(1, 1)[e ? "animate" : "css"](h, a.animate);
                if (b === "min" && this.orientation === "horizontal") this.range.stop(1, 1)[e ? "animate" : "css"]({
                    width: f + "%"
                }, a.animate);
                if (b === "max" && this.orientation === "horizontal") this.range[e ? "animate" : "css"]({
                    width: 100 - f + "%"
                }, {
                    queue: false,
                    duration: a.animate
                });
                if (b === "min" && this.orientation === "vertical") this.range.stop(1, 1)[e ? "animate" : "css"]({
                    height: f + "%"
                }, a.animate);
                if (b === "max" && this.orientation === "vertical") this.range[e ? "animate" : "css"]({
                    height: 100 - f + "%"
                }, {
                    queue: false,
                    duration: a.animate
                })
            }
        }
    });
    d.extend(d.ui.slider, {
        version: "1.8.8"
    })
})(jQuery);;
(function (d, p) {
    function u() {
        return ++v
    }
    function w() {
        return ++x
    }
    var v = 0,
        x = 0;
    d.widget("ui.tabs", {
        options: {
            add: null,
            ajaxOptions: null,
            cache: false,
            cookie: null,
            collapsible: false,
            disable: null,
            disabled: [],
            enable: null,
            event: "click",
            fx: null,
            idPrefix: "ui-tabs-",
            load: null,
            panelTemplate: "<div></div>",
            remove: null,
            select: null,
            show: null,
            spinner: "<em>Loading&#8230;</em>",
            tabTemplate: "<li><a href='#{href}'><span>#{label}</span></a></li>"
        },
        _create: function () {
            this._tabify(true)
        },
        _setOption: function (b, e) {
            if (b == "selected") this.options.collapsible && e == this.options.selected || this.select(e);
            else {
                this.options[b] = e;
                this._tabify()
            }
        },
        _tabId: function (b) {
            return b.title && b.title.replace(/\s/g, "_").replace(/[^\w\u00c0-\uFFFF-]/g, "") || this.options.idPrefix + u()
        },
        _sanitizeSelector: function (b) {
            return b.replace(/:/g, "\\:")
        },
        _cookie: function () {
            var b = this.cookie || (this.cookie = this.options.cookie.name || "ui-tabs-" + w());
            return d.cookie.apply(null, [b].concat(d.makeArray(arguments)))
        },
        _ui: function (b, e) {
            return {
                tab: b,
                panel: e,
                index: this.anchors.index(b)
            }
        },
        _cleanup: function () {
            this.lis.filter(".ui-state-processing").removeClass("ui-state-processing").find("span:data(label.tabs)").each(function () {
                var b = d(this);
                b.html(b.data("label.tabs")).removeData("label.tabs")
            })
        },
        _tabify: function (b) {
            function e(g, f) {
                g.css("display", "");
                !d.support.opacity && f.opacity && g[0].style.removeAttribute("filter")
            }
            var a = this,
                c = this.options,
                h = /^#.+/;
            this.list = this.element.find("ol,ul").eq(0);
            this.lis = d(" > li:has(a[href])", this.list);
            this.anchors = this.lis.map(function () {
                return d("a", this)[0]
            });
            this.panels = d([]);
            this.anchors.each(function (g, f) {
                var i = d(f).attr("href"),
                    l = i.split("#")[0],
                    q;
                if (l && (l === location.toString().split("#")[0] || (q = d("base")[0]) && l === q.href)) {
                    i = f.hash;
                    f.href = i
                }
                if (h.test(i)) a.panels = a.panels.add(a.element.find(a._sanitizeSelector(i)));
                else if (i && i !== "#") {
                    d.data(f, "href.tabs", i);
                    d.data(f, "load.tabs", i.replace(/#.*$/, ""));
                    i = a._tabId(f);
                    f.href = "#" + i;
                    f = a.element.find("#" + i);
                    if (!f.length) {
                        f = d(c.panelTemplate).attr("id", i).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").insertAfter(a.panels[g - 1] || a.list);
                        f.data("destroy.tabs", true)
                    }
                    a.panels = a.panels.add(f)
                } else c.disabled.push(g)
            });
            if (b) {
                this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all");
                this.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");
                this.lis.addClass("ui-state-default ui-corner-top");
                this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom");
                if (c.selected === p) {
                    location.hash && this.anchors.each(function (g, f) {
                        if (f.hash == location.hash) {
                            c.selected = g;
                            return false
                        }
                    });
                    if (typeof c.selected !== "number" && c.cookie) c.selected = parseInt(a._cookie(), 10);
                    if (typeof c.selected !== "number" && this.lis.filter(".ui-tabs-selected").length) c.selected = this.lis.index(this.lis.filter(".ui-tabs-selected"));
                    c.selected = c.selected || (this.lis.length ? 0 : -1)
                } else if (c.selected === null) c.selected = -1;
                c.selected = c.selected >= 0 && this.anchors[c.selected] || c.selected < 0 ? c.selected : 0;
                c.disabled = d.unique(c.disabled.concat(d.map(this.lis.filter(".ui-state-disabled"), function (g) {
                    return a.lis.index(g)
                }))).sort();
                d.inArray(c.selected, c.disabled) != -1 && c.disabled.splice(d.inArray(c.selected, c.disabled), 1);
                this.panels.addClass("ui-tabs-hide");
                this.lis.removeClass("ui-tabs-selected ui-state-active");
                if (c.selected >= 0 && this.anchors.length) {
                    a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash)).removeClass("ui-tabs-hide");
                    this.lis.eq(c.selected).addClass("ui-tabs-selected ui-state-active");
                    a.element.queue("tabs", function () {
                        a._trigger("show", null, a._ui(a.anchors[c.selected], a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash))))
                    });
                    this.load(c.selected)
                }
                d(window).bind("unload", function () {
                    a.lis.add(a.anchors).unbind(".tabs");
                    a.lis = a.anchors = a.panels = null
                })
            } else c.selected = this.lis.index(this.lis.filter(".ui-tabs-selected"));
            this.element[c.collapsible ? "addClass" : "removeClass"]("ui-tabs-collapsible");
            c.cookie && this._cookie(c.selected, c.cookie);
            b = 0;
            for (var j; j = this.lis[b]; b++) d(j)[d.inArray(b, c.disabled) != -1 && !d(j).hasClass("ui-tabs-selected") ? "addClass" : "removeClass"]("ui-state-disabled");
            c.cache === false && this.anchors.removeData("cache.tabs");
            this.lis.add(this.anchors).unbind(".tabs");
            if (c.event !== "mouseover") {
                var k = function (g, f) {
                        f.is(":not(.ui-state-disabled)") && f.addClass("ui-state-" + g)
                    },
                    n = function (g, f) {
                        f.removeClass("ui-state-" + g)
                    };
                this.lis.bind("mouseover.tabs", function () {
                    k("hover", d(this))
                });
                this.lis.bind("mouseout.tabs", function () {
                    n("hover", d(this))
                });
                this.anchors.bind("focus.tabs", function () {
                    k("focus", d(this).closest("li"))
                });
                this.anchors.bind("blur.tabs", function () {
                    n("focus", d(this).closest("li"))
                })
            }
            var m, o;
            if (c.fx) if (d.isArray(c.fx)) {
                m = c.fx[0];
                o = c.fx[1]
            } else m = o = c.fx;
            var r = o ?
            function (g, f) {
                d(g).closest("li").addClass("ui-tabs-selected ui-state-active");
                f.hide().removeClass("ui-tabs-hide").animate(o, o.duration || "normal", function () {
                    e(f, o);
                    a._trigger("show", null, a._ui(g, f[0]))
                })
            } : function (g, f) {
                d(g).closest("li").addClass("ui-tabs-selected ui-state-active");
                f.removeClass("ui-tabs-hide");
                a._trigger("show", null, a._ui(g, f[0]))
            }, s = m ?
            function (g, f) {
                f.animate(m, m.duration || "normal", function () {
                    a.lis.removeClass("ui-tabs-selected ui-state-active");
                    f.addClass("ui-tabs-hide");
                    e(f, m);
                    a.element.dequeue("tabs")
                })
            } : function (g, f) {
                a.lis.removeClass("ui-tabs-selected ui-state-active");
                f.addClass("ui-tabs-hide");
                a.element.dequeue("tabs")
            };
            this.anchors.bind(c.event + ".tabs", function () {
                var g = this,
                    f = d(g).closest("li"),
                    i = a.panels.filter(":not(.ui-tabs-hide)"),
                    l = a.element.find(a._sanitizeSelector(g.hash));
                if (f.hasClass("ui-tabs-selected") && !c.collapsible || f.hasClass("ui-state-disabled") || f.hasClass("ui-state-processing") || a.panels.filter(":animated").length || a._trigger("select", null, a._ui(this, l[0])) === false) {
                    this.blur();
                    return false
                }
                c.selected = a.anchors.index(this);
                a.abort();
                if (c.collapsible) if (f.hasClass("ui-tabs-selected")) {
                    c.selected = -1;
                    c.cookie && a._cookie(c.selected, c.cookie);
                    a.element.queue("tabs", function () {
                        s(g, i)
                    }).dequeue("tabs");
                    this.blur();
                    return false
                } else if (!i.length) {
                    c.cookie && a._cookie(c.selected, c.cookie);
                    a.element.queue("tabs", function () {
                        r(g, l)
                    });
                    a.load(a.anchors.index(this));
                    this.blur();
                    return false
                }
                c.cookie && a._cookie(c.selected, c.cookie);
                if (l.length) {
                    i.length && a.element.queue("tabs", function () {
                        s(g, i)
                    });
                    a.element.queue("tabs", function () {
                        r(g, l)
                    });
                    a.load(a.anchors.index(this))
                } else throw "jQuery UI Tabs: Mismatching fragment identifier.";
                d.browser.msie && this.blur()
            });
            this.anchors.bind("click.tabs", function () {
                return false
            })
        },
        _getIndex: function (b) {
            if (typeof b == "string") b = this.anchors.index(this.anchors.filter("[href$=" + b + "]"));
            return b
        },
        destroy: function () {
            var b = this.options;
            this.abort();
            this.element.unbind(".tabs").removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible").removeData("tabs");
            this.list.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");
            this.anchors.each(function () {
                var e = d.data(this, "href.tabs");
                if (e) this.href = e;
                var a = d(this).unbind(".tabs");
                d.each(["href", "load", "cache"], function (c, h) {
                    a.removeData(h + ".tabs")
                })
            });
            this.lis.unbind(".tabs").add(this.panels).each(function () {
                d.data(this, "destroy.tabs") ? d(this).remove() : d(this).removeClass("ui-state-default ui-corner-top ui-tabs-selected ui-state-active ui-state-hover ui-state-focus ui-state-disabled ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide")
            });
            b.cookie && this._cookie(null, b.cookie);
            return this
        },
        add: function (b, e, a) {
            if (a === p) a = this.anchors.length;
            var c = this,
                h = this.options;
            e = d(h.tabTemplate.replace(/#\{href\}/g, b).replace(/#\{label\}/g, e));
            b = !b.indexOf("#") ? b.replace("#", "") : this._tabId(d("a", e)[0]);
            e.addClass("ui-state-default ui-corner-top").data("destroy.tabs", true);
            var j = c.element.find("#" + b);
            j.length || (j = d(h.panelTemplate).attr("id", b).data("destroy.tabs", true));
            j.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide");
            if (a >= this.lis.length) {
                e.appendTo(this.list);
                j.appendTo(this.list[0].parentNode)
            } else {
                e.insertBefore(this.lis[a]);
                j.insertBefore(this.panels[a])
            }
            h.disabled = d.map(h.disabled, function (k) {
                return k >= a ? ++k : k
            });
            this._tabify();
            if (this.anchors.length == 1) {
                h.selected = 0;
                e.addClass("ui-tabs-selected ui-state-active");
                j.removeClass("ui-tabs-hide");
                this.element.queue("tabs", function () {
                    c._trigger("show", null, c._ui(c.anchors[0], c.panels[0]))
                });
                this.load(0)
            }
            this._trigger("add", null, this._ui(this.anchors[a], this.panels[a]));
            return this
        },
        remove: function (b) {
            b = this._getIndex(b);
            var e = this.options,
                a = this.lis.eq(b).remove(),
                c = this.panels.eq(b).remove();
            if (a.hasClass("ui-tabs-selected") && this.anchors.length > 1) this.select(b + (b + 1 < this.anchors.length ? 1 : -1));
            e.disabled = d.map(d.grep(e.disabled, function (h) {
                return h != b
            }), function (h) {
                return h >= b ? --h : h
            });
            this._tabify();
            this._trigger("remove", null, this._ui(a.find("a")[0], c[0]));
            return this
        },
        enable: function (b) {
            b = this._getIndex(b);
            var e = this.options;
            if (d.inArray(b, e.disabled) != -1) {
                this.lis.eq(b).removeClass("ui-state-disabled");
                e.disabled = d.grep(e.disabled, function (a) {
                    return a != b
                });
                this._trigger("enable", null, this._ui(this.anchors[b], this.panels[b]));
                return this
            }
        },
        disable: function (b) {
            b = this._getIndex(b);
            var e = this.options;
            if (b != e.selected) {
                this.lis.eq(b).addClass("ui-state-disabled");
                e.disabled.push(b);
                e.disabled.sort();
                this._trigger("disable", null, this._ui(this.anchors[b], this.panels[b]))
            }
            return this
        },
        select: function (b) {
            b = this._getIndex(b);
            if (b == -1) if (this.options.collapsible && this.options.selected != -1) b = this.options.selected;
            else return this;
            this.anchors.eq(b).trigger(this.options.event + ".tabs");
            return this
        },
        load: function (b) {
            b = this._getIndex(b);
            var e = this,
                a = this.options,
                c = this.anchors.eq(b)[0],
                h = d.data(c, "load.tabs");
            this.abort();
            if (!h || this.element.queue("tabs").length !== 0 && d.data(c, "cache.tabs")) this.element.dequeue("tabs");
            else {
                this.lis.eq(b).addClass("ui-state-processing");
                if (a.spinner) {
                    var j = d("span", c);
                    j.data("label.tabs", j.html()).html(a.spinner)
                }
                this.xhr = d.ajax(d.extend({}, a.ajaxOptions, {
                    url: h,
                    success: function (k, n) {
                        e.element.find(e._sanitizeSelector(c.hash)).html(k);
                        e._cleanup();
                        a.cache && d.data(c, "cache.tabs", true);
                        e._trigger("load", null, e._ui(e.anchors[b], e.panels[b]));
                        try {
                            a.ajaxOptions.success(k, n)
                        } catch (m) {}
                    },
                    error: function (k, n) {
                        e._cleanup();
                        e._trigger("load", null, e._ui(e.anchors[b], e.panels[b]));
                        try {
                            a.ajaxOptions.error(k, n, b, c)
                        } catch (m) {}
                    }
                }));
                e.element.dequeue("tabs");
                return this
            }
        },
        abort: function () {
            this.element.queue([]);
            this.panels.stop(false, true);
            this.element.queue("tabs", this.element.queue("tabs").splice(-2, 2));
            if (this.xhr) {
                this.xhr.abort();
                delete this.xhr
            }
            this._cleanup();
            return this
        },
        url: function (b, e) {
            this.anchors.eq(b).removeData("cache.tabs").data("load.tabs", e);
            return this
        },
        length: function () {
            return this.anchors.length
        }
    });
    d.extend(d.ui.tabs, {
        version: "1.8.8"
    });
    d.extend(d.ui.tabs.prototype, {
        rotation: null,
        rotate: function (b, e) {
            var a = this,
                c = this.options,
                h = a._rotate || (a._rotate = function (j) {
                    clearTimeout(a.rotation);
                    a.rotation = setTimeout(function () {
                        var k = c.selected;
                        a.select(++k < a.anchors.length ? k : 0)
                    }, b);
                    j && j.stopPropagation()
                });
            e = a._unrotate || (a._unrotate = !e ?
            function (j) {
                j.clientX && a.rotate(null)
            } : function () {
                t = c.selected;
                h()
            });
            if (b) {
                this.element.bind("tabsshow", h);
                this.anchors.bind(c.event + ".tabs", e);
                h()
            } else {
                clearTimeout(a.rotation);
                this.element.unbind("tabsshow", h);
                this.anchors.unbind(c.event + ".tabs", e);
                delete this._rotate;
                delete this._unrotate
            }
            return this
        }
    })
})(jQuery);;
jQuery.effects ||
function (f, j) {
    function n(c) {
        var a;
        if (c && c.constructor == Array && c.length == 3) return c;
        if (a = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c)) return [parseInt(a[1], 10), parseInt(a[2], 10), parseInt(a[3], 10)];
        if (a = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c)) return [parseFloat(a[1]) * 2.55, parseFloat(a[2]) * 2.55, parseFloat(a[3]) * 2.55];
        if (a = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c)) return [parseInt(a[1], 16), parseInt(a[2], 16), parseInt(a[3], 16)];
        if (a = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c)) return [parseInt(a[1] + a[1], 16), parseInt(a[2] + a[2], 16), parseInt(a[3] + a[3], 16)];
        if (/rgba\(0, 0, 0, 0\)/.exec(c)) return o.transparent;
        return o[f.trim(c).toLowerCase()]
    }
    function s(c, a) {
        var b;
        do {
            b = f.curCSS(c, a);
            if (b != "" && b != "transparent" || f.nodeName(c, "body")) break;
            a = "backgroundColor"
        } while (c = c.parentNode);
        return n(b)
    }
    function p() {
        var c = document.defaultView ? document.defaultView.getComputedStyle(this, null) : this.currentStyle,
            a = {},
            b, d;
        if (c && c.length && c[0] && c[c[0]]) for (var e = c.length; e--;) {
            b = c[e];
            if (typeof c[b] == "string") {
                d = b.replace(/\-(\w)/g, function (g, h) {
                    return h.toUpperCase()
                });
                a[d] = c[b]
            }
        } else for (b in c) if (typeof c[b] === "string") a[b] = c[b];
        return a
    }
    function q(c) {
        var a, b;
        for (a in c) {
            b = c[a];
            if (b == null || f.isFunction(b) || a in t || /scrollbar/.test(a) || !/color/i.test(a) && isNaN(parseFloat(b))) delete c[a]
        }
        return c
    }
    function u(c, a) {
        var b = {
            _: 0
        },
            d;
        for (d in a) if (c[d] != a[d]) b[d] = a[d];
        return b
    }
    function k(c, a, b, d) {
        if (typeof c == "object") {
            d = a;
            b = null;
            a = c;
            c = a.effect
        }
        if (f.isFunction(a)) {
            d = a;
            b = null;
            a = {}
        }
        if (typeof a == "number" || f.fx.speeds[a]) {
            d = b;
            b = a;
            a = {}
        }
        if (f.isFunction(b)) {
            d = b;
            b = null
        }
        a = a || {};
        b = b || a.duration;
        b = f.fx.off ? 0 : typeof b == "number" ? b : b in f.fx.speeds ? f.fx.speeds[b] : f.fx.speeds._default;
        d = d || a.complete;
        return [c, a, b, d]
    }
    function m(c) {
        if (!c || typeof c === "number" || f.fx.speeds[c]) return true;
        if (typeof c === "string" && !f.effects[c]) return true;
        return false
    }
    f.effects = {};
    f.each(["backgroundColor", "borderBottomColor", "borderLeftColor", "borderRightColor", "borderTopColor", "borderColor", "color", "outlineColor"], function (c, a) {
        f.fx.step[a] = function (b) {
            if (!b.colorInit) {
                b.start = s(b.elem, a);
                b.end = n(b.end);
                b.colorInit = true
            }
            b.elem.style[a] = "rgb(" + Math.max(Math.min(parseInt(b.pos * (b.end[0] - b.start[0]) + b.start[0], 10), 255), 0) + "," + Math.max(Math.min(parseInt(b.pos * (b.end[1] - b.start[1]) + b.start[1], 10), 255), 0) + "," + Math.max(Math.min(parseInt(b.pos * (b.end[2] - b.start[2]) + b.start[2], 10), 255), 0) + ")"
        }
    });
    var o = {
        aqua: [0, 255, 255],
        azure: [240, 255, 255],
        beige: [245, 245, 220],
        black: [0, 0, 0],
        blue: [0, 0, 255],
        brown: [165, 42, 42],
        cyan: [0, 255, 255],
        darkblue: [0, 0, 139],
        darkcyan: [0, 139, 139],
        darkgrey: [169, 169, 169],
        darkgreen: [0, 100, 0],
        darkkhaki: [189, 183, 107],
        darkmagenta: [139, 0, 139],
        darkolivegreen: [85, 107, 47],
        darkorange: [255, 140, 0],
        darkorchid: [153, 50, 204],
        darkred: [139, 0, 0],
        darksalmon: [233, 150, 122],
        darkviolet: [148, 0, 211],
        fuchsia: [255, 0, 255],
        gold: [255, 215, 0],
        green: [0, 128, 0],
        indigo: [75, 0, 130],
        khaki: [240, 230, 140],
        lightblue: [173, 216, 230],
        lightcyan: [224, 255, 255],
        lightgreen: [144, 238, 144],
        lightgrey: [211, 211, 211],
        lightpink: [255, 182, 193],
        lightyellow: [255, 255, 224],
        lime: [0, 255, 0],
        magenta: [255, 0, 255],
        maroon: [128, 0, 0],
        navy: [0, 0, 128],
        olive: [128, 128, 0],
        orange: [255, 165, 0],
        pink: [255, 192, 203],
        purple: [128, 0, 128],
        violet: [128, 0, 128],
        red: [255, 0, 0],
        silver: [192, 192, 192],
        white: [255, 255, 255],
        yellow: [255, 255, 0],
        transparent: [255, 255, 255]
    },
        r = ["add", "remove", "toggle"],
        t = {
            border: 1,
            borderBottom: 1,
            borderColor: 1,
            borderLeft: 1,
            borderRight: 1,
            borderTop: 1,
            borderWidth: 1,
            margin: 1,
            padding: 1
        };
    f.effects.animateClass = function (c, a, b, d) {
        if (f.isFunction(b)) {
            d = b;
            b = null
        }
        return this.queue("fx", function () {
            var e = f(this),
                g = e.attr("style") || " ",
                h = q(p.call(this)),
                l, v = e.attr("className");
            f.each(r, function (w, i) {
                c[i] && e[i + "Class"](c[i])
            });
            l = q(p.call(this));
            e.attr("className", v);
            e.animate(u(h, l), a, b, function () {
                f.each(r, function (w, i) {
                    c[i] && e[i + "Class"](c[i])
                });
                if (typeof e.attr("style") == "object") {
                    e.attr("style").cssText = "";
                    e.attr("style").cssText = g
                } else e.attr("style", g);
                d && d.apply(this, arguments)
            });
            h = f.queue(this);
            l = h.splice(h.length - 1, 1)[0];
            h.splice(1, 0, l);
            f.dequeue(this)
        })
    };
    f.fn.extend({
        _addClass: f.fn.addClass,
        addClass: function (c, a, b, d) {
            return a ? f.effects.animateClass.apply(this, [{
                add: c
            },
            a, b, d]) : this._addClass(c)
        },
        _removeClass: f.fn.removeClass,
        removeClass: function (c, a, b, d) {
            return a ? f.effects.animateClass.apply(this, [{
                remove: c
            },
            a, b, d]) : this._removeClass(c)
        },
        _toggleClass: f.fn.toggleClass,
        toggleClass: function (c, a, b, d, e) {
            return typeof a == "boolean" || a === j ? b ? f.effects.animateClass.apply(this, [a ? {
                add: c
            } : {
                remove: c
            },
            b, d, e]) : this._toggleClass(c, a) : f.effects.animateClass.apply(this, [{
                toggle: c
            },
            a, b, d])
        },
        switchClass: function (c, a, b, d, e) {
            return f.effects.animateClass.apply(this, [{
                add: a,
                remove: c
            },
            b, d, e])
        }
    });
    f.extend(f.effects, {
        version: "1.8.8",
        save: function (c, a) {
            for (var b = 0; b < a.length; b++) a[b] !== null && c.data("ec.storage." + a[b], c[0].style[a[b]])
        },
        restore: function (c, a) {
            for (var b = 0; b < a.length; b++) a[b] !== null && c.css(a[b], c.data("ec.storage." + a[b]))
        },
        setMode: function (c, a) {
            if (a == "toggle") a = c.is(":hidden") ? "show" : "hide";
            return a
        },
        getBaseline: function (c, a) {
            var b;
            switch (c[0]) {
            case "top":
                b = 0;
                break;
            case "middle":
                b = 0.5;
                break;
            case "bottom":
                b = 1;
                break;
            default:
                b = c[0] / a.height
            }
            switch (c[1]) {
            case "left":
                c = 0;
                break;
            case "center":
                c = 0.5;
                break;
            case "right":
                c = 1;
                break;
            default:
                c = c[1] / a.width
            }
            return {
                x: c,
                y: b
            }
        },
        createWrapper: function (c) {
            if (c.parent().is(".ui-effects-wrapper")) return c.parent();
            var a = {
                width: c.outerWidth(true),
                height: c.outerHeight(true),
                "float": c.css("float")
            },
                b = f("<div></div>").addClass("ui-effects-wrapper").css({
                    fontSize: "100%",
                    background: "transparent",
                    border: "none",
                    margin: 0,
                    padding: 0
                });
            c.wrap(b);
            b = c.parent();
            if (c.css("position") == "static") {
                b.css({
                    position: "relative"
                });
                c.css({
                    position: "relative"
                })
            } else {
                f.extend(a, {
                    position: c.css("position"),
                    zIndex: c.css("z-index")
                });
                f.each(["top", "left", "bottom", "right"], function (d, e) {
                    a[e] = c.css(e);
                    if (isNaN(parseInt(a[e], 10))) a[e] = "auto"
                });
                c.css({
                    position: "relative",
                    top: 0,
                    left: 0,
                    right: "auto",
                    bottom: "auto"
                })
            }
            return b.css(a).show()
        },
        removeWrapper: function (c) {
            if (c.parent().is(".ui-effects-wrapper")) return c.parent().replaceWith(c);
            return c
        },
        setTransition: function (c, a, b, d) {
            d = d || {};
            f.each(a, function (e, g) {
                unit = c.cssUnit(g);
                if (unit[0] > 0) d[g] = unit[0] * b + unit[1]
            });
            return d
        }
    });
    f.fn.extend({
        effect: function (c) {
            var a = k.apply(this, arguments),
                b = {
                    options: a[1],
                    duration: a[2],
                    callback: a[3]
                };
            a = b.options.mode;
            var d = f.effects[c];
            if (f.fx.off || !d) return a ? this[a](b.duration, b.callback) : this.each(function () {
                b.callback && b.callback.call(this)
            });
            return d.call(this, b)
        },
        _show: f.fn.show,
        show: function (c) {
            if (m(c)) return this._show.apply(this, arguments);
            else {
                var a = k.apply(this, arguments);
                a[1].mode = "show";
                return this.effect.apply(this, a)
            }
        },
        _hide: f.fn.hide,
        hide: function (c) {
            if (m(c)) return this._hide.apply(this, arguments);
            else {
                var a = k.apply(this, arguments);
                a[1].mode = "hide";
                return this.effect.apply(this, a)
            }
        },
        __toggle: f.fn.toggle,
        toggle: function (c) {
            if (m(c) || typeof c === "boolean" || f.isFunction(c)) return this.__toggle.apply(this, arguments);
            else {
                var a = k.apply(this, arguments);
                a[1].mode = "toggle";
                return this.effect.apply(this, a)
            }
        },
        cssUnit: function (c) {
            var a = this.css(c),
                b = [];
            f.each(["em", "px", "%", "pt"], function (d, e) {
                if (a.indexOf(e) > 0) b = [parseFloat(a), e]
            });
            return b
        }
    });
    f.easing.jswing = f.easing.swing;
    f.extend(f.easing, {
        def: "easeOutQuad",
        swing: function (c, a, b, d, e) {
            return f.easing[f.easing.def](c, a, b, d, e)
        },
        easeInQuad: function (c, a, b, d, e) {
            return d * (a /= e) * a + b
        },
        easeOutQuad: function (c, a, b, d, e) {
            return -d * (a /= e) * (a - 2) + b
        },
        easeInOutQuad: function (c, a, b, d, e) {
            if ((a /= e / 2) < 1) return d / 2 * a * a + b;
            return -d / 2 * (--a * (a - 2) - 1) + b
        },
        easeInCubic: function (c, a, b, d, e) {
            return d * (a /= e) * a * a + b
        },
        easeOutCubic: function (c, a, b, d, e) {
            return d * ((a = a / e - 1) * a * a + 1) + b
        },
        easeInOutCubic: function (c, a, b, d, e) {
            if ((a /= e / 2) < 1) return d / 2 * a * a * a + b;
            return d / 2 * ((a -= 2) * a * a + 2) + b
        },
        easeInQuart: function (c, a, b, d, e) {
            return d * (a /= e) * a * a * a + b
        },
        easeOutQuart: function (c, a, b, d, e) {
            return -d * ((a = a / e - 1) * a * a * a - 1) + b
        },
        easeInOutQuart: function (c, a, b, d, e) {
            if ((a /= e / 2) < 1) return d / 2 * a * a * a * a + b;
            return -d / 2 * ((a -= 2) * a * a * a - 2) + b
        },
        easeInQuint: function (c, a, b, d, e) {
            return d * (a /= e) * a * a * a * a + b
        },
        easeOutQuint: function (c, a, b, d, e) {
            return d * ((a = a / e - 1) * a * a * a * a + 1) + b
        },
        easeInOutQuint: function (c, a, b, d, e) {
            if ((a /= e / 2) < 1) return d / 2 * a * a * a * a * a + b;
            return d / 2 * ((a -= 2) * a * a * a * a + 2) + b
        },
        easeInSine: function (c, a, b, d, e) {
            return -d * Math.cos(a / e * (Math.PI / 2)) + d + b
        },
        easeOutSine: function (c, a, b, d, e) {
            return d * Math.sin(a / e * (Math.PI / 2)) + b
        },
        easeInOutSine: function (c, a, b, d, e) {
            return -d / 2 * (Math.cos(Math.PI * a / e) - 1) + b
        },
        easeInExpo: function (c, a, b, d, e) {
            return a == 0 ? b : d * Math.pow(2, 10 * (a / e - 1)) + b
        },
        easeOutExpo: function (c, a, b, d, e) {
            return a == e ? b + d : d * (-Math.pow(2, -10 * a / e) + 1) + b
        },
        easeInOutExpo: function (c, a, b, d, e) {
            if (a == 0) return b;
            if (a == e) return b + d;
            if ((a /= e / 2) < 1) return d / 2 * Math.pow(2, 10 * (a - 1)) + b;
            return d / 2 * (-Math.pow(2, -10 * --a) + 2) + b
        },
        easeInCirc: function (c, a, b, d, e) {
            return -d * (Math.sqrt(1 - (a /= e) * a) - 1) + b
        },
        easeOutCirc: function (c, a, b, d, e) {
            return d * Math.sqrt(1 - (a = a / e - 1) * a) + b
        },
        easeInOutCirc: function (c, a, b, d, e) {
            if ((a /= e / 2) < 1) return -d / 2 * (Math.sqrt(1 - a * a) - 1) + b;
            return d / 2 * (Math.sqrt(1 - (a -= 2) * a) + 1) + b
        },
        easeInElastic: function (c, a, b, d, e) {
            c = 1.70158;
            var g = 0,
                h = d;
            if (a == 0) return b;
            if ((a /= e) == 1) return b + d;
            g || (g = e * 0.3);
            if (h < Math.abs(d)) {
                h = d;
                c = g / 4
            } else c = g / (2 * Math.PI) * Math.asin(d / h);
            return -(h * Math.pow(2, 10 * (a -= 1)) * Math.sin((a * e - c) * 2 * Math.PI / g)) + b
        },
        easeOutElastic: function (c, a, b, d, e) {
            c = 1.70158;
            var g = 0,
                h = d;
            if (a == 0) return b;
            if ((a /= e) == 1) return b + d;
            g || (g = e * 0.3);
            if (h < Math.abs(d)) {
                h = d;
                c = g / 4
            } else c = g / (2 * Math.PI) * Math.asin(d / h);
            return h * Math.pow(2, -10 * a) * Math.sin((a * e - c) * 2 * Math.PI / g) + d + b
        },
        easeInOutElastic: function (c, a, b, d, e) {
            c = 1.70158;
            var g = 0,
                h = d;
            if (a == 0) return b;
            if ((a /= e / 2) == 2) return b + d;
            g || (g = e * 0.3 * 1.5);
            if (h < Math.abs(d)) {
                h = d;
                c = g / 4
            } else c = g / (2 * Math.PI) * Math.asin(d / h);
            if (a < 1) return -0.5 * h * Math.pow(2, 10 * (a -= 1)) * Math.sin((a * e - c) * 2 * Math.PI / g) + b;
            return h * Math.pow(2, -10 * (a -= 1)) * Math.sin((a * e - c) * 2 * Math.PI / g) * 0.5 + d + b
        },
        easeInBack: function (c, a, b, d, e, g) {
            if (g == j) g = 1.70158;
            return d * (a /= e) * a * ((g + 1) * a - g) + b
        },
        easeOutBack: function (c, a, b, d, e, g) {
            if (g == j) g = 1.70158;
            return d * ((a = a / e - 1) * a * ((g + 1) * a + g) + 1) + b
        },
        easeInOutBack: function (c, a, b, d, e, g) {
            if (g == j) g = 1.70158;
            if ((a /= e / 2) < 1) return d / 2 * a * a * (((g *= 1.525) + 1) * a - g) + b;
            return d / 2 * ((a -= 2) * a * (((g *= 1.525) + 1) * a + g) + 2) + b
        },
        easeInBounce: function (c, a, b, d, e) {
            return d - f.easing.easeOutBounce(c, e - a, 0, d, e) + b
        },
        easeOutBounce: function (c, a, b, d, e) {
            return (a /= e) < 1 / 2.75 ? d * 7.5625 * a * a + b : a < 2 / 2.75 ? d * (7.5625 * (a -= 1.5 / 2.75) * a + 0.75) + b : a < 2.5 / 2.75 ? d * (7.5625 * (a -= 2.25 / 2.75) * a + 0.9375) + b : d * (7.5625 * (a -= 2.625 / 2.75) * a + 0.984375) + b
        },
        easeInOutBounce: function (c, a, b, d, e) {
            if (a < e / 2) return f.easing.easeInBounce(c, a * 2, 0, d, e) * 0.5 + b;
            return f.easing.easeOutBounce(c, a * 2 - e, 0, d, e) * 0.5 + d * 0.5 + b
        }
    })
}(jQuery);;
(function (e) {
    e.effects.bounce = function (b) {
        return this.queue(function () {
            var a = e(this),
                l = ["position", "top", "bottom", "left", "right"],
                h = e.effects.setMode(a, b.options.mode || "effect"),
                d = b.options.direction || "up",
                c = b.options.distance || 20,
                m = b.options.times || 5,
                i = b.duration || 250;
            /show|hide/.test(h) && l.push("opacity");
            e.effects.save(a, l);
            a.show();
            e.effects.createWrapper(a);
            var f = d == "up" || d == "down" ? "top" : "left";
            d = d == "up" || d == "left" ? "pos" : "neg";
            c = b.options.distance || (f == "top" ? a.outerHeight({
                margin: true
            }) / 3 : a.outerWidth({
                margin: true
            }) / 3);
            if (h == "show") a.css("opacity", 0).css(f, d == "pos" ? -c : c);
            if (h == "hide") c /= m * 2;
            h != "hide" && m--;
            if (h == "show") {
                var g = {
                    opacity: 1
                };
                g[f] = (d == "pos" ? "+=" : "-=") + c;
                a.animate(g, i / 2, b.options.easing);
                c /= 2;
                m--
            }
            for (g = 0; g < m; g++) {
                var j = {},
                    k = {};
                j[f] = (d == "pos" ? "-=" : "+=") + c;
                k[f] = (d == "pos" ? "+=" : "-=") + c;
                a.animate(j, i / 2, b.options.easing).animate(k, i / 2, b.options.easing);
                c = h == "hide" ? c * 2 : c / 2
            }
            if (h == "hide") {
                g = {
                    opacity: 0
                };
                g[f] = (d == "pos" ? "-=" : "+=") + c;
                a.animate(g, i / 2, b.options.easing, function () {
                    a.hide();
                    e.effects.restore(a, l);
                    e.effects.removeWrapper(a);
                    b.callback && b.callback.apply(this, arguments)
                })
            } else {
                j = {};
                k = {};
                j[f] = (d == "pos" ? "-=" : "+=") + c;
                k[f] = (d == "pos" ? "+=" : "-=") + c;
                a.animate(j, i / 2, b.options.easing).animate(k, i / 2, b.options.easing, function () {
                    e.effects.restore(a, l);
                    e.effects.removeWrapper(a);
                    b.callback && b.callback.apply(this, arguments)
                })
            }
            a.queue("fx", function () {
                a.dequeue()
            });
            a.dequeue()
        })
    }
})(jQuery);;

(function ($) {
    $.fn.endlessScroll = function (options) {
        var defaults = {
            pixelOffset: 5,
            data: "",
            callback: function () {
                return true;
            }
        },
            options = $.extend(defaults, options),
            scroll_direction_down, last_scroll_position = 0,
            content_height = 0,
            sum_top = 0,
            visible_height = 0,
            pagination_elem_id, current_page = options.start_page,
            was_at_top = true;
        $(document).unbind('essl_trigger_' + $(this).attr('id'));
        $(document).bind('essl_trigger_' + $(this).attr('id'), function (evt, data) {
            last_scroll_position = data.scroll_pos;
            was_at_top = data.is_at_top;
            if (data.current_page != undefined) {
                current_page = data.current_page;
            }
        });
        this.scroll = function () {
            if (EndlessScroll.isLocked()) {
                return;
            }
            var has_height, is_at_bottom, is_at_top;
            scroll_direction_down = ($(this).scrollTop() - last_scroll_position) > 0;
            last_scroll_position = $(this).scrollTop();
            if (sum_top == 0) {
                $('#' + options.insert_elem_id).children('.top').each(function (key, elem) {
                    sum_top += $(elem).height();
                });
            }
            if (sum_top > 20) {
                current_page = EndlessScroll.updatePage(pagination_elem_id, current_page, was_at_top, visible_height, last_scroll_position, sum_top);
                was_at_top = sum_top - (visible_height / 2) > last_scroll_position;
                EndlessScroll.unlockPage();
            }
            if (content_height == 0) {
                $('#' + options.insert_elem_id).children().each(function (key, elem) {
                    content_height += $(elem).outerHeight();
                });
            }
            has_height = content_height > 0;
            is_at_bottom = $(this).scrollTop() >= content_height - $(this).height() - options.pixelOffset;
            is_at_top = $(this).scrollTop() < options.pixelOffset;
            if (has_height && (is_at_top || is_at_bottom)) {
                if (typeof options.data == 'function') {
                    data = options.data.apply(this);
                } else {
                    data = options.data;
                }
                if (data !== false) {
                    var args = new Array(scroll_direction_down),
                        pagination = options.callback.apply(this, args);
                    sum_top = 0;
                    content_height = 0;
                    if (pagination) {
                        visible_height = pagination.visible_height;
                        pagination_elem_id = pagination.pagination_elem_id;
                    }
                }
            }
        }
        $(this).scroll(this.scroll);
    };
})(jQuery);

$.fn.draggable2 = function (options) {
    options = $.extend({
        distance: 0,
        live: true,
        draggingClass: "dragging"
    }, options);
    var offset, margins, startPos, downEvt, helper, passedDistance, dropTargets, currentDropTarget, draggable, tmpOpts = [];
    $(this)[options.live ? 'live' : 'bind']("mousedown.draggable2", function (e) {
        if ($(this).hasClass(options.draggingClass) || (helper && passedDistance) || e.metaKey || e.shiftKey || e.ctrlKey) return;
        draggable = $(this);
        margins = {
            left: (parseInt(draggable.css("marginLeft"), 10) || 0),
            top: (parseInt(draggable.css("marginTop"), 10) || 0)
        };
        offset = draggable.offset();
        offset = {
            top: e.pageY - offset.top + margins.top,
            left: e.pageX - offset.left + margins.left
        };
        if ($.isFunction(options.helper)) {
            helper = options.helper.call(draggable, function (option, value) {
                options[option] = value;
                tmpOpts.push(option);
            }, e);
            if (!helper) throw ("DOM node not returned from helper function");
        } else {
            helper = draggable.clone();
        }
        helper.addClass(options.draggingClass).css({
            position: "absolute"
        });
        startPos = {
            top: e.pageY - offset.top + "px",
            left: e.pageX - offset.left + "px"
        };
        $(document).bind("mousemove.draggable2", drag).bind("mouseup.draggable2", dragup);
        dropTargets = [];
        var targets = $.dd.targets.join(",");
        if (targets.length > 0) {
            $(targets).each(function (i) {
                var self = $(this);
                var opts = self.data("drop_options");
                if (opts.accept && !draggable.is(opts.accept)) return;
                var o = self.offset();
                dropTargets.push({
                    x: o.left,
                    y: o.top,
                    width: self.outerWidth(),
                    height: self.outerHeight(),
                    el: self,
                    index: i,
                    options: opts
                });
            });
        }
        downEvt = e;
        e.preventDefault();
    });

    function drag(e) {
        if (!passedDistance) {
            if (Math.max(Math.abs(downEvt.pageX - e.pageX), Math.abs(downEvt.pageY - e.pageY)) >= options.distance) {
                passedDistance = true;
                if (options.cursorAt) {
                    if (options.cursorAt.top) offset.top = options.cursorAt.top + margins.top;
                    if (options.cursorAt.left) offset.left = options.cursorAt.left + margins.left;
                }
                helper.appendTo("body");
            } else return;
        }
        helper.css({
            top: e.pageY - offset.top + "px",
            left: e.pageX - offset.left + "px"
        });
        if (currentDropTarget) {
            var cur = currentDropTarget;
            if (!(e.pageX > cur.x && e.pageX < cur.x + cur.width && e.pageY > cur.y && e.pageY < cur.y + cur.height)) {
                cur.el.removeClass(cur.options.hoverClass);
                currentDropTarget = false;
                return;
            }
        }
        $.each(dropTargets, function (i) {
            if (e.pageX > this.x && e.pageX < this.x + this.width && (e.pageY > this.y && e.pageY < this.y + this.height)) {
                currentDropTarget = this;
                this.el.addClass(currentDropTarget.options.hoverClass);
                return false;
            }
        });
    }

    function dragup(e) {
        $(document).unbind("mousemove.draggable2", drag).unbind("mouseup.draggable2", dragup);
        if (currentDropTarget) {
            helper.remove();
            currentDropTarget.el.removeClass(currentDropTarget.options.hoverClass);
            try {
                if ($.isFunction(currentDropTarget.options.drop)) {
                    currentDropTarget.options.drop.call(currentDropTarget.el, {
                        helper: helper,
                        draggable: draggable,
                        position: {
                            x: e.pageX,
                            y: e.pageY
                        }
                    });
                }
            } catch (e) {}
            cleanUpVars();
        } else {
            helper.animate(startPos, function () {
                $(this).remove();
            });
            cleanUpVars();
        }
    }

    function cleanUpVars() {
        $.each(tmpOpts, function () {
            delete options[this];
        });
        tmpOpts = [];
        offset = margins = startPos = downEvt = helper = passedDistance = dropTargets = currentDropTarget = draggable = null;
    }
    if ($.browser.msie) {
        $(this).attr('unselectable', 'on');
    }
    return this;
};
$.fn.droppable2 = function (options) {
    options = $.extend({
        hoverClass: 'draghovered'
    }, options);
    var self = $(this);
    self.data("drop_options", options);
    $.dd.targets.push(this.selector);
    return this;
};
$.dd = {
    targets: []
};

(function ($) {
    $.fn.autoResizeIt = function (options) {
        var settings = $.extend({
            maxHeight: 190
        }, options),
            textarea = this.css('overflow', 'hidden'),
            startHeight = textarea.height(),
            prevScrollTop, scrollTop, clone = (function () {
                var props = ['height', 'width', 'lineHeight', 'textDecoration', 'letterSpacing'],
                    txt_props = {
                        position: 'absolute',
                        top: 0,
                        left: -666666
                    },
                    i, l;
                for (i = 0, l = props.length; i < l; i++) {
                    txt_props[props[i]] = textarea.css(props[i]);
                }
                return textarea.clone().removeAttr('name').removeAttr('id').css(txt_props).attr('tabIndex', '-1').insertBefore(textarea);
            })(),
            redraw = function () {
                clone.height(0).val(textarea.val()).scrollTop(100000);
                scrollTop = Math.max(clone.scrollTop(), startHeight);
                if (prevScrollTop == scrollTop) {
                    return;
                }
                prevScrollTop = scrollTop;
                if (scrollTop >= settings.maxHeight) {
                    textarea.css('overflow-y', 'auto');
                    return;
                } else {
                    textarea.css('overflow-y', 'hidden');
                }
                textarea.height(scrollTop);
                clone.height(scrollTop);
            };
        textarea.bind('keydown', redraw).bind('change', redraw);
        return this;
    };
})(jQuery);

var JSON;
if (!JSON) {
    JSON = {};
}(function () {
    "use strict";

    function f(n) {
        return n < 10 ? '0' + n : n;
    }
    if (typeof Date.prototype.toJSON !== 'function') {
        Date.prototype.toJSON = function (key) {
            return isFinite(this.valueOf()) ? this.getUTCFullYear() + '-' + f(this.getUTCMonth() + 1) + '-' + f(this.getUTCDate()) + 'T' + f(this.getUTCHours()) + ':' + f(this.getUTCMinutes()) + ':' + f(this.getUTCSeconds()) + 'Z' : null;
        };
        String.prototype.toJSON = Number.prototype.toJSON = Boolean.prototype.toJSON = function (key) {
            return this.valueOf();
        };
    }
    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        gap, indent, meta = {
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"': '\\"',
            '\\': '\\\\'
        },
        rep;

    function quote(string) {
        escapable.lastIndex = 0;
        return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
            var c = meta[a];
            return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
        }) + '"' : '"' + string + '"';
    }

    function str(key, holder) {
        var i, k, v, length, mind = gap,
            partial, value = holder[key];
        if (value && typeof value === 'object' && typeof value.toJSON === 'function') {
            value = value.toJSON(key);
        }
        if (typeof rep === 'function') {
            value = rep.call(holder, key, value);
        }
        switch (typeof value) {
        case 'string':
            return quote(value);
        case 'number':
            return isFinite(value) ? String(value) : 'null';
        case 'boolean':
        case 'null':
            return String(value);
        case 'object':
            if (!value) {
                return 'null';
            }
            gap += indent;
            partial = [];
            if (Object.prototype.toString.apply(value) === '[object Array]') {
                length = value.length;
                for (i = 0; i < length; i += 1) {
                    partial[i] = str(i, value) || 'null';
                }
                v = partial.length === 0 ? '[]' : gap ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : '[' + partial.join(',') + ']';
                gap = mind;
                return v;
            }
            if (rep && typeof rep === 'object') {
                length = rep.length;
                for (i = 0; i < length; i += 1) {
                    if (typeof rep[i] === 'string') {
                        k = rep[i];
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            } else {
                for (k in value) {
                    if (Object.prototype.hasOwnProperty.call(value, k)) {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            }
            v = partial.length === 0 ? '{}' : gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : '{' + partial.join(',') + '}';
            gap = mind;
            return v;
        }
    }
    if (typeof JSON.stringify !== 'function') {
        JSON.stringify = function (value, replacer, space) {
            var i;
            gap = '';
            indent = '';
            if (typeof space === 'number') {
                for (i = 0; i < space; i += 1) {
                    indent += ' ';
                }
            } else if (typeof space === 'string') {
                indent = space;
            }
            rep = replacer;
            if (replacer && typeof replacer !== 'function' && (typeof replacer !== 'object' || typeof replacer.length !== 'number')) {
                throw new Error('JSON.stringify');
            }
            return str('', {
                '': value
            });
        };
    }
    if (typeof JSON.parse !== 'function') {
        JSON.parse = function (text, reviver) {
            var j;

            function walk(holder, key) {
                var k, v, value = holder[key];
                if (value && typeof value === 'object') {
                    for (k in value) {
                        if (Object.prototype.hasOwnProperty.call(value, k)) {
                            v = walk(value, k);
                            if (v !== undefined) {
                                value[k] = v;
                            } else {
                                delete value[k];
                            }
                        }
                    }
                }
                return reviver.call(holder, key, value);
            }
            text = String(text);
            cx.lastIndex = 0;
            if (cx.test(text)) {
                text = text.replace(cx, function (a) {
                    return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
                });
            }
            if (/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
                j = eval('(' + text + ')');
                return typeof reviver === 'function' ? walk({
                    '': j
                }, '') : j;
            }
            throw new SyntaxError('JSON.parse');
        };
    }
}());

(function () {
    var cache = {};
    this.tmpl2 = function (template, data) {
        return tmpl(template, data);
    }
    this.tmpl = function (str, data) {
        str.replace(/\/\*<!\[CDATA\[\*\//, "").replace(/\/\*]]>\*\//, "").replace(/\/\/<!\[CDATA\[/, "").replace(/\/\/\]\]>/, "")
        return tmpl(str, data);
    }

    function tmpl(str, data) {
        var fn = !/\W/.test(str) ? cache[str] = cache[str] || tmpl(str) : new Function("obj", "var p=[],print=function(){p.push.apply(p,arguments);};" + "with(obj){p.push('" + str.replace(/[\r\t\n]/g, " ").split("<%").join("\t").replace(/((^|%>)[^\t]*)'/g, "$1\r").replace(/\t=(.*?)%>/g, "',$1,'").split("\t").join("');").split("%>").join("p.push('").split("\r").join("\\'") + "');}return p.join('');");
        return data ? fn(data) : fn;
    };
})();

function ngettext(s, p, n) {
    return (n == 1) ? s : p[Number(n != 1)];
}
var Timestamp = {
    last_servertime_update: 0,
    server: function () {
        try {
            var diff = 0;
            if (this.last_servertime_update != 0) {
                diff = ~~ (this.client() - this.last_servertime_update);
            }
            return (Game.server_time + diff);
        } catch (e) {}
    },
    client: function () {
        return Timestamp.make();
    },
    clientServerDiff: function () {
        return Timestamp.client() - Timestamp.server();
    },
    now: function (which) {
        switch (which) {
        case 'client':
        case 'c':
            return Timestamp.client();
        case 'server':
        case 's':
        default:
            return Timestamp.server();
        }
    },
    serverGMTOffset: function () {
        var o = Game.server_gmt_offset;
        o = -1 * parseInt(o);
        return o;
    },
    localeGMTOffset: function () {
        var o = Game.locale_gmt_offset;
        o = -1 * parseInt(o);
        return o;
    },
    clientGMTOffset: function () {
        var o = new Date();
        o = -1 * o.getTimezoneOffset() * 60;
        return o;
    },
    updateServerTime: function (date) {
        try {
            var ts = Timestamp.make(date);
            Game.server_time = ts;
            this.last_servertime_update = this.client();
        } catch (e) {}
    },
    updateServerTimebyUnixTime: function (timestamp) {
        Game.server_time = ~~timestamp;
        this.last_servertime_update = this.client();
    },
    make: function (d) {
        d = (undefined === d) ? new Date() : new Date(d);
        return ~~ (d.getTime() / 1000);
    },
    toDate: function (ts) {
        return new Date(ts * 1000);
    },
    shiftUnixTimestampByTimezoneOffset: function (unix_timestamp, timezone_type) {
        return unix_timestamp + (timezone_type == 'lc_timezone' ? Timestamp.localeGMTOffset() : Timestamp.serverGMTOffset());
    }
};

var Ajax = {};
Ajax.request_running = {};
Ajax.get = function (controller, action, params, callbackSuccess, options, lock, show_ajax_loader) {
    Ajax.ajax(controller, action, params, callbackSuccess, options, "GET", lock, show_ajax_loader, "json");
}
Ajax.post = function (controller, action, params, callbackSuccess, options, lock, show_ajax_loader) {
    Ajax.ajax(controller, action, params, callbackSuccess, options, "POST", lock, show_ajax_loader, "json");
}
Ajax.postEx = function (controller, action, params, responseType, callbackSuccess, options, lock, show_ajax_loader) {
    Ajax.ajax(controller, action, params, callbackSuccess, options, "POST", lock, show_ajax_loader, responseType);
}
Ajax.getEx = function (controller, action, params, responseType, callbackSuccess, options, lock, show_ajax_loader) {
    Ajax.ajax(controller, action, params, callbackSuccess, options, "GET", lock, show_ajax_loader, responseType);
}
Ajax.ajax = function (controller, action, params, callbackSuccess, options, type, lock, show_ajax_loader, responseType) {
    if (typeof show_ajax_loader == 'undefined') {
        show_ajax_loader = true;
    }
    if (typeof lock != 'undefined' && Ajax.request_running[lock]) {
        return;
    }
    Ajax.request_running[lock] = true;
    var u = url(controller, action);
    var data = {
        json: JSON.stringify(params)
    };
    if (show_ajax_loader) {
        Layout.showAjaxLoader();
    }
    options = jQuery.extend({
        url: u,
        type: type,
        data: data,
        dataType: responseType,
        success: function (data, flag) {
            if (show_ajax_loader) {
                Layout.hideAjaxLoader();
            }
            Ajax.request_running[lock] = false;
            callbackSuccess.call(this, data, flag);
        },
        error: function () {
            if (show_ajax_loader) {
                Layout.hideAjaxLoader();
            }
            Ajax.request_running[lock] = false;
        }
    }, options);
    jQuery.ajaxSetup({
        'cache': false
    });
    jQuery.ajax(options);
}

function gpAjax(layout, locking) {
    var locking = locking;
    var request_running = false;
    var layout = layout;

    function gpajaxrequest(controller, action, params, type, callback, show_loader) {
        if (typeof show_loader == 'undefined') {
            show_loader = true;
        }
        if (locking == true && request_running != false) {
            return;
        }
        request_running = true;
        var callback_success = null;
        var callback_error = null;
        if (typeof callback == 'object') {
            callback_success = callback.success ? callback.success : null;
            callback_error = callback.error ? callback.error : null;
        } else {
            callback_success = callback;
        }
        if (layout && show_loader == true) {
            layout.showAjaxLoader();
        }
        if (layout && layout.isMaintenanceBlocked && layout.isMaintenanceBlocked()) {
            return;
        }
        jQuery.ajaxSetup({
            cache: false
        });
        jQuery.ajax({
            url: url(controller, action),
            type: type,
            data: {
                json: JSON.stringify(params)
            },
            dataType: 'text',
            success: function (data, flag) {
                var retdata;
                var offset = data.indexOf('\n');
                var json_length = ~~data.substr(0, offset++);
                var json_data = data.substr(offset, json_length);
                offset += json_length;
                retdata = $.parseJSON(json_data);
                if (typeof retdata !== 'object' || retdata == null) {
                    retdata = {
                        data: retdata
                    };
                }
                data = data.substr(offset, data.length - offset);
                offset = 0;
                while (data.length !== 0) {
                    var offset = data.indexOf('\n');
                    var lengths = data.substr(0, offset++).split('|');
                    if (lengths.length !== 2) {
                        if (layout && layout.HumanMessage) {
                            layout.HumanMessage.error('Invalid Response');
                        } else {
                            HumanMessage.error('Invalid Response');
                        }
                        break;
                    }
                    lengths[0] = ~~lengths[0];
                    lengths[1] = ~~lengths[1];
                    retdata[data.substr(offset, lengths[0])] = data.substr(offset + lengths[0], lengths[1]);
                    offset += (lengths[0] + lengths[1]);
                    data = data.substr(offset, data.length - offset);
                }
                request_running = false;
                if (layout && show_loader == true) {
                    layout.hideAjaxLoader();
                }
                Timestamp.updateServerTimebyUnixTime(retdata._srvtime);
                delete retdata._srvtime;
                if (retdata.notifications) {
                    Layout.recvNotifyData(retdata);
                    delete retdata.notifications;
                    delete retdata.next_fetch_in;
                }
                if (retdata._sysmsg == 1) {
                    Layout.resetNotificationRequestTimeout();
                }
                delete retdata._sysmsg;
                var hmsg;
                if (layout && layout.HumanMessage) {
                    hmsg = layout.HumanMessage;
                } else {
                    hmsg = HumanMessage;
                }
                if (retdata.redirect) {
                    window.location.href = retdata.redirect;
                    return;
                }
                if (retdata.maintenance) {
                    Layout.blockForMaintenance(retdata.maintenance);
                }
                if (retdata.error) {
                    if (callback_error) {
                        callback_error(layout, retdata);
                    }
                    hmsg.error(retdata.error);
                    return;
                }
                if (retdata.tutorial_error) {
                    hmsg.error(retdata.tutorial_error);
                }
                if (retdata.success) {
                    hmsg.success(retdata.success);
                }
                if (retdata.bar && hasValidTownToken(retdata)) {
                    Layout.updateBar(retdata.bar);
                }
                if (callback_success) {
                    callback_success(layout, retdata, flag);
                }
            },
            error: function () {
                if (layout && layout.HumanMessage) {
                    layout.HumanMessage.error('Response Error');
                } else {
                    HumanMessage.error('Response Error');
                }
                if (layout && show_loader == true) {
                    layout.hideAjaxLoader();
                }
                request_running = false;
                if (callback_error) {
                    callback_error(layout, null);
                }
            }
        });
    }
    this.get = function (controller, action, params, show_loader, callbackSuccess) {
        params.legacy = true;
        gpajaxrequest(controller, action, params, 'GET', callbackSuccess, show_loader);
    };
    this.post = function (controller, action, params, show_loader, callbackSuccess) {
        params.legacy = true;
        gpajaxrequest(controller, action, params, 'POST', callbackSuccess, show_loader);
    };
    this.tmpGet = function (controller, action, params, show_loader, callbackSuccess) {
        gpajaxrequest(controller, action, params, 'GET', callbackSuccess, show_loader);
    };
    this.tmpPost = function (controller, action, params, show_loader, callbackSuccess) {
        gpajaxrequest(controller, action, params, 'POST', callbackSuccess, show_loader);
    };
}

Function.prototype.inherits = function (F) {
    this.prototype = new F;
    this.prototype.constructor = this;
    this.prototype.parent = F.prototype;
};
Array.prototype.remove = function (from, to) {
    var rest = this.slice((to || from) + 1 || this.length);
    this.length = from < 0 ? this.length + from : from;
    return this.push.apply(this, rest);
};
Array.prototype.max = function (array) {
    return Math.max.apply(Math, array);
};
Array.prototype.min = function (array) {
    return Math.min.apply(Math, array);
};
if (!Array.prototype.indexOf) {
    Array.prototype.indexOf = function (searchElement) {
        "use strict";
        var t, len, n;
        if (this === void 0 || this === null) {
            throw new TypeError();
        }
        t = Object(this);
        len = t.length >>> 0;
        if (len === 0) {
            return -1;
        }
        n = 0;
        if (arguments.length > 0) {
            n = Number(arguments[1]);
            if (n !== n) {
                n = 0;
            } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
                n = (n > 0 || -1) * Math.floor(Math.abs(n));
            }
        }
        if (n >= len) {
            return -1;
        }
        var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
        for (; k < len; k++) {
            if (k in t && t[k] === searchElement) {
                return k;
            }
        }
        return -1;
    };
}
if (!Array.prototype.map) {
    Array.prototype.map = function (f, args) {
        var l = this.length,
            i;
        if (typeof f != "function") {
            throw new TypeError();
        }
        var result = new Array(l);
        args = arguments[1];
        for (i = 0; i < l; i++) {
            if (i in this) {
                result[i] = f.call(args, this[i], i, this);
            }
        }
        return result;
    };
}
if (!Array.prototype.filter) {
    Array.prototype.filter = function (fun) {
        "use strict";
        if (this === void 0 || this === null) throw new TypeError();
        var t = Object(this);
        var len = t.length >>> 0;
        if (typeof fun !== "function") {
            throw new TypeError();
        }
        var res = [];
        var thisp = arguments[1];
        for (var i = 0; i < len; i++) {
            if (i in t) {
                var val = t[i];
                if (fun.call(thisp, val, i, t)) {
                    res.push(val);
                }
            }
        }
        return res;
    };
}
if (typeof atob !== 'function') {
    function atob(str) {
        var key_str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
            output = '',
            i = 0,
            len = str.length;
        var enc1, enc2, enc3, enc4;
        var chr1, chr2, chr3;
        str = str.replace(/[^A-Za-z0-9\+\/\=]/g, "");
        while (i < len) {
            enc1 = key_str.indexOf(str.charAt(i++));
            enc2 = key_str.indexOf(str.charAt(i++));
            enc3 = key_str.indexOf(str.charAt(i++));
            enc4 = key_str.indexOf(str.charAt(i++));
            chr1 = (enc1 << 2) | (enc2 >> 4);
            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
            chr3 = ((enc3 & 3) << 6) | enc4;
            output += String.fromCharCode(chr1);
            if (enc3 != 64) {
                output = output + String.fromCharCode(chr2);
            }
            if (enc4 != 64) {
                output = output + String.fromCharCode(chr3);
            }
        }
        return utf8Decode(output);
    }
}
if (typeof btoa !== 'function') {
    function btoa(str) {
        var key_str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
            output = '',
            chr1, chr2, chr3, enc1, enc2, enc3, enc4, i = 0;
        str = utf8Encode(str);
        while (i < str.length) {
            chr1 = str.charCodeAt(i++);
            chr2 = str.charCodeAt(i++);
            chr3 = str.charCodeAt(i++);
            enc1 = chr1 >> 2;
            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
            enc4 = chr3 & 63;
            if (isNaN(chr2)) {
                enc3 = enc4 = 64;
            } else if (isNaN(chr3)) {
                enc4 = 64;
            }
            output += key_str.charAt(enc1) + key_str.charAt(enc2) + key_str.charAt(enc3) + key_str.charAt(enc4);
        }
        return output;
    }
}

function utf8Encode(string) {
    string = string.replace(/\r\n/g, "\n");
    var utftext = "",
        n;
    for (n = 0; n < string.length; n++) {
        var c = string.charCodeAt(n);
        if (c < 128) {
            utftext += String.fromCharCode(c);
        } else if ((c > 127) && (c < 2048)) {
            utftext += String.fromCharCode((c >> 6) | 192);
            utftext += String.fromCharCode((c & 63) | 128);
        } else {
            utftext += String.fromCharCode((c >> 12) | 224);
            utftext += String.fromCharCode(((c >> 6) & 63) | 128);
            utftext += String.fromCharCode((c & 63) | 128);
        }
    }
    return utftext;
}

function utf8Decode(utftext) {
    var string = "";
    var i = 0,
        c, c3, c2;
    c = c3 = c2 = 0;
    while (i < utftext.length) {
        c = utftext.charCodeAt(i);
        if (c < 128) {
            string += String.fromCharCode(c);
            i++;
        } else if ((c > 191) && (c < 224)) {
            c2 = utftext.charCodeAt(i + 1);
            string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
            i += 2;
        } else {
            c2 = utftext.charCodeAt(i + 1);
            c3 = utftext.charCodeAt(i + 2);
            string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
            i += 3;
        }
    }
    return string;
}
jQuery.fn.extend({
    unselectable: function () {
        this.css("-moz-user-select", 'none');
        this.css("unselectable", 'yes');
    }
});
jQuery.blocker = function (options) {
    var settings = jQuery.extend({
        id: '',
        caching: options.id,
        html: 'f00',
        width: 520,
        height: 200,
        gameloader: false,
        bgClickable: true,
        success: '',
        onSuccess: function () {},
        cancel: '',
        onCancel: function () {},
        callback: void(0)
    }, options);
    var block_wrapper = '<div class="gpwindow_frame" style="position: absolute; top: ' + (($(window).height() - settings.height) / 2) + 'px;' + 'left: ' + (($(window).width() - settings.width) / 2) + 'px;' + 'width: ' + settings.width + 'px; height: ' + settings.height + 'px; z-index: 5000; display: block;">' + '<div class="gpwindow_left"></div><div class="gpwindow_right"></div><div class="gpwindow_bottom"><div class="gpwindow_left corner"></div><div class="gpwindow_right corner">' + '</div></div><div class="gpwindow_top"><div class="gpwindow_left corner"></div><div class="gpwindow_right corner"></div></div>' + '<div class="gpwindow_content"></div>';
    var frame = $(block_wrapper);
    var elm = {
        box: frame,
        bg: $('<div id="blockbox_bg"></div>'),
        body: $('body')
    };
    elm.content = elm.box.find('div.gpwindow_content');
    this.blocker.block = function () {
        var tmp = settings.html.parent();
        elm.original_parent = tmp.length ? tmp : $('body');
        elm.html = settings.html.detach();
        elm.content.append(elm.html.show());
        elm.box.appendTo(elm.body).show();
        elm.bg.appendTo(elm.body).show();
    };
    this.blocker.handleEvents = function () {
        if (settings.bgClickable) {
            elm.bg.bind('click.block', function () {
                jQuery.blocker.unblock()
            });
        }
        $(settings.success).unbind('click').click(function () {
            settings.onSuccess();
            jQuery.blocker.unblock();
        });
        $(settings.cancel).unbind('click').click(function () {
            settings.onCancel();
            jQuery.blocker.unblock();
        });
    };
    this.blocker.unblock = function () {
        elm.box.hide().detach();
        elm.bg.hide().detach();
        if (settings.gameloader) {
            elm.html.remove();
        } else {
            elm.html.appendTo(elm.original_parent).hide();
        }
        if (settings.callback && typeof settings.callback === 'function') {
            settings.callback();
        }
    };
    this.blocker.block();
    this.blocker.handleEvents();
};
Function.prototype.bind = function (obj) {
    var that = this;
    return function () {
        return that.apply(obj, arguments);
    }
};

function buildLink(controller, parameters) {
    var params = [],
        key;
    for (key in parameters) {
        params.push(key + "=" + escape(parameters[key]));
    }
    if (typeof (parameters.action) !== undefined) {
        params.push("h=" + encodeURIComponent(Game.csrfToken));
    }
    return '/game/' + controller + '?' + params.join("&");
}

function url(controller, action, parameters) {
    if (controller && controller.substr(0, 1) == "/") {
        return controller;
    }
    controller = controller || Game.controller;
    parameters = parameters || {};
    if (action != undefined && action != '') {
        parameters.action = action;
    }
    parameters.town_id = parameters.town_id || Game.townId;
    return buildLink(controller, parameters);
}

function submit_form(form, controller, action, parameters) {
    parameters = parameters || {};
    if (action != undefined && action != '') {
        parameters.action = action;
    }
    parameters.town_id = parameters.town_id || Game.townId;
    document.getElementById(form).setAttribute('action', buildLink(controller, parameters));
    $("#" + form).submit();
    return false;
}

function submit_form_light(form) {
    $("#" + form).submit();
    return false;
}

function submit_post(controller, action, post_params) {
    post_params = post_params || {};
    var form = $('<form id="temp_submit_form" method="POST" action="?"></form>');
    $.each(post_params, function (name, value) {
        form.append('<input type="hidden" name="' + name + '" value="' + value + '"/>');
    });
    $('body').append(form);
    submit_form('temp_submit_form', controller, action);
    return false;
}

function readableSeconds(seconds) {
    var hours, minutes;
    hours = ~~ (seconds / 3600);
    minutes = ~~ ((seconds - (hours * 3600)) / 60);
    seconds = ~~ (seconds % 60);

    function zerofill(n, len) {
        if (typeof (len) == 'undefined') {
            len = 2;
        }
        n = n + '';
        while (n.length < len) {
            n = '0' + n;
        }
        return n;
    }
    return hours + ':' + zerofill(minutes) + ':' + zerofill(seconds);
}

function readableDate(date, utc, ext_date, with_seconds) {
    return readableUnixTimestamp(date.getTime(), (utc == true ? 'no_offset' : 'player_timezone'), {
        extended_date: ext_date,
        with_seconds: with_seconds
    });
}

function readableUnixTimestamp(timestamp, timezone_type, options) {
    options = options === undefined ? {} : options;
    var with_seconds = options.with_seconds === undefined ? true : options.with_seconds;
    var extended_date = options.extended_date === undefined ? false : options.extended_date;
    var ts = (timezone_type == 'no_offset' ? timestamp : Timestamp.shiftUnixTimestampByTimezoneOffset(timestamp, timezone_type));
    var date = new Date(ts * 1E3);
    var hours, minutes, seconds, days, months;
    var result;
    hours = date.getUTCHours();
    minutes = date.getUTCMinutes();
    seconds = date.getUTCSeconds();
    if (minutes < 10) {
        minutes = '0' + minutes;
    }
    if (seconds < 10) {
        seconds = '0' + seconds;
    }
    if (extended_date) {
        days = date.getUTCDate();
        months = date.getUTCMonth() + 1;
        if (days < 10) {
            days = '0' + days;
        }
        if (months < 10) {
            months = '0' + months;
        }
        result = days + '.' + months + '.|' + hours + ':' + minutes + (with_seconds ? (':' + seconds) : '');
    } else {
        result = hours + ':' + minutes + (with_seconds ? (':' + seconds) : '');
    }
    return result;
}

function formatDateTimeNice(timestamp, with_seconds, timezone_type) {
    var result, split;
    var cur = new Date(Timestamp.shiftUnixTimestampByTimezoneOffset(Timestamp.server(), timezone_type) * 1E3);
    var fut = new Date(Timestamp.shiftUnixTimestampByTimezoneOffset(timestamp, timezone_type) * 1E3);
    var curY = cur.getFullYear();
    var curM = cur.getMonth();
    var curD = cur.getDate();
    var today = new Date(curY, curM, curD, 23, 59, 59).getTime();
    var tomorrow = new Date(curY, curM, curD + 1, 23, 59, 59).getTime();
    if (fut.getTime() <= today) {
        result = s('today at %1 ', readableUnixTimestamp(timestamp, timezone_type, {
            with_seconds: with_seconds
        }));
    } else if (fut.getTime() <= tomorrow) {
        result = s('tomorrow at %1 ', readableUnixTimestamp(timestamp, timezone_type, {
            with_seconds: with_seconds
        }));
    } else {
        split = readableUnixTimestamp(timestamp, timezone_type, {
            extended_date: true,
            with_seconds: with_seconds
        }).split('|');
        result = s('on %1 at %2', split[0], split[1]);
    }
    return result;
}

function _(s) {
    return s;
}

function s(text) {
    var i;
    if (!text) {
        return '';
    }
    for (i = 1; i < arguments.length; i++) {
        text = text.split('%' + i).join(arguments[i]);
    }
    return text;
}

function bound(value, min, max) {
    return Math.min(Math.max(value, min), max);
}
jQuery.httpData = function (xhr, type, s) {
    return xhr.responseText;
}

function parseToValidNumericValue(ele) {
    var value = $(ele).val();
    value = value.replace(/[^\d\.]/g, '');
    value = parseInt(value, 10);
    if (isNaN(value)) {
        value = '';
    }
    $(ele).val(value);
};

function paginator_goto(element, page_offset) {
    var page = parseInt(prompt("Go to page"), 10) - 1;
    if (isNaN(page)) {
        return false;
    }
    var offset = page * page_offset;
    var tabs = false;
    if (arguments.length == 5) {
        tabs = true;
        var tab_box_id = arguments[2];
        var controller = arguments[3];
        var action = arguments[4];
    }
    if (offset < 0) {
        return false;
    }
    element = $(element);
    if (tabs === true) {
        paginatorTabsGotoPage(tab_box_id, page, controller, action);
    } else {
        var href = element.attr('href');
        element.attr('href', href.replace('offset=0', 'offset=' + offset));
    }
    return true;
}

function paginatorTabsGotoPagePrompt(tab_box_id, message_id, controller, action) {
    GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_MESSAGE).ajaxRequestGet('message', 'gotopage', {
        tab_box_id: tab_box_id,
        message_id: message_id,
        controller: controller,
        action: action
    }, function (response, data, status) {
        if (status == "success") {
            Layout.dialogWindow.open(data.html, "Go to page", 300, 140, function () {}, true);
        }
    });
}

function paginatorTabsGotoPage(tab_box_id, page_offset, message_id, controller, action) {
    var params = {
        offset: ((page_offset * 10) - 10)
    };
    if (isNumber(message_id)) {
        params.id = message_id;
    }
    Layout.ajaxGet(controller, action, params, true, function (return_data) {
        $('#' + tab_box_id).html(return_data.html);
        var wrapper;
        wrapper = $('#' + tab_box_id).find('div.bb_button_wrapper');
        new BBCode(Layout.wnd.getOpenFirst(Layout.wnd.TYPE_MESSAGE), wrapper, '#message_reply_message');
    });
}

function debug(whatever) {
    var i, s;
    if (!Game.dev) {
        return;
    }
    try {
        if (arguments.length > 1) {
            console.group();
            for (i = 0; i < arguments.length; i++) {
                console.debug(arguments[i]);
            }
            console.groupEnd();
        } else {
            console.log(whatever);
        }
    } catch (x) {
        try {
            opera.postError(whatever);
        } catch (x) {
            if ('object' == typeof (whatever)) {
                s = '';
                for (i in whatever) {
                    s += i + ': ' + whatever[i] + "\n";
                }
                alert(s);
            } else {
                alert(whatever);
            }
        }
    }
}
jQuery.cookie = function (name, value, options) {
    var expires, date, i;
    if (typeof value != 'undefined') {
        options = options || {};
        if (value === null) {
            value = '';
            options.expires = -1;
        }
        expires = '';
        if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
            if (typeof options.expires == 'number') {
                date = new Date();
                date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
            } else {
                date = options.expires;
            }
            expires = '; expires=' + date.toUTCString();
        }
        var path = options.path ? '; path=' + (options.path) : '';
        var domain = options.domain ? '; domain=' + (options.domain) : '';
        var secure = options.secure ? '; secure' : '';
        document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
    } else {
        var cookieValue = null;
        if (document.cookie && document.cookie != '') {
            var cookies = document.cookie.split(';');
            for (i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                if (cookie.substring(0, name.length + 1) == (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
};
String.prototype.strip = function () {
    return this.replace(/<(.|\n)*?>/g, '');
};
String.prototype.isLTE = function (y) {
    var x = this,
        x_m = x.match(/^\s+\d+|^\d+/),
        y_m = y.match(/^\s+\d+|^\d+/),
        x_i = x_m != null ? parseInt(x_m.shift(), 10) : NaN,
        y_i = y_m != null ? parseInt(y_m.shift(), 10) : NaN;
    if (isNaN(x_i) || isNaN(y_i)) {
        x = x.toLowerCase();
        y = y.toLowerCase();
    } else {
        x = x_i;
        y = y_i;
    }
    return x <= y;
};
Date.prototype.toShortString = function () {
    var h, m, d, mn;
    h = ((h = this.getHours()) < 10 ? '0' + h : h);
    m = ((m = this.getMinutes()) < 10 ? '0' + m : m);
    d = ((d = this.getDate()) < 10 ? '0' + d : d);
    mn = ((mn = this.getMonth() + 1) < 10 ? '0' + mn : mn);
    return (h + ':' + m + ' ' + d + '/' + mn + '/' + this.getFullYear());
};
jQuery.extend(jQuery.easing, {
    bounce: function (x, t, b, c, d) {
        if ((t /= d) < (1 / 2.75)) {
            return c * (7.5625 * t * t) + b;
        } else if (t < (2 / 2.75)) {
            return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
        } else if (t < (2.5 / 2.75)) {
            return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
        } else {
            return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
        }
    }
});

function callstacktrace() {
    debug('Begin of Stacktrace');
    var m = arguments.callee.caller;
    var i = 0;
    while (m) {
        debug('[' + i + ']: ' + (m.name ? m.name : '<anonymous>'));
        i--;
        m = m.caller;
    }
    debug('-------------___');
}

function w(foo) {
    var elm = foo || this;
    if (elm.wnd) {
        return elm.wnd;
    }
    while (elm.parentNode && ((elm = elm.parentNode).tagName !== 'DIV' ? elm = elm.parentNode : elm).id.indexOf('gpwnd') === -1) {
        null;
    }
    var id = (~~elm.id.match(/\d+/));
    elm.wnd = Layout.wnd.GetByID(id);
    return elm.wnd;
}

function ClickAreaCoordinateTool() {
    var t = $('#town_background');
    var foo = $(document.createElement('div')).css({
        'position': 'relative',
        'width': '100%',
        'height': '100%',
        'zIndex': 10
    });
    var coords = [],
        i = 0;

    function create(e) {
        i++;
        var x = e.layerX,
            y = e.layerY;
        coords.push({
            'x': x,
            'y': y
        });
        var div = $(document.createElement('div'));
        div.css({
            'position': 'absolute',
            'left': x - 2,
            'top': y - 2,
            'width': 5,
            'height': 5,
            'backgroundColor': '#fc6',
            'color': '#fff',
            'cursor': 'pointer'
        }).text(i).attr('id', i);
        div.draggable({
            'stop': function (e) {
                var i = parseInt(this.id, 10);
                foo[i] = {
                    'x': parseInt(this.style.left, 10),
                    'y': parseInt(this.style.top, 10)
                };
            }
        });
        foo.append(div);
        return false;
    }

    function print() {
        var str = '',
            i;
        for (i = 0; i < coords.length; i++) {
            str += coords[i].x + ',' + coords[i].y + ',';
        }
        debug(str);
    }

    function button() {
        var bar = $(document.createElement('div'));
        bar.css({
            'position': 'absolute',
            'left': 0,
            'bottom': 0,
            'width': 20,
            'height': 20,
            'backgroundColor': '#f00'
        }).click(print);
        foo.append(bar);
    }
    t.append(foo);
    foo.click(create);
    button();
}

function attr(attribs) {
    var res = '',
        att;
    for (att in attribs) {
        if (attribs[att]) {
            res = res + ' ' + att + '="' + attribs[att] + '"';
        }
    }
    return res;
}

function button(text, html_options, as_string, wnd) {
    var btn, button_text, callback, that = this;
    if (!html_options.href) {
        html_options.href = "#";
    }
    if ((callback = html_options.onclick) && typeof callback == 'function') {
        as_string = false;
        delete html_options.onclick;
    }
    html_options = attr(html_options);
    button_text = '<a class="button" ' + html_options + '>' + '<span class="left"><span class="right">' + '<span class="middle">' + text + '</span>' + '</span></span>' + '<span style="clear:both;"></span>' + '</a>';
    if (!as_string) {
        btn = $(button_text);
    }
    if (callback && !as_string) {
        btn.click(function () {
            callback.apply(wnd);
        });
    }
    return as_string ? button_text : btn;
}

function countProperties(o) {
    var n = 0,
        key;
    for (key in o) {
        n++;
    }
    return n;
}

function isEmpty(map) {
    var key;
    for (key in map) {
        if (map.hasOwnProperty(key)) {
            return false;
        }
    }
    return true;
}

function isNumber(val) {
    return typeof val === 'number' && isFinite(val);
}

function is_array(o) {
    return typeof o === 'object' && (o instanceof Array);
}

function addIsNavalToGameDataUnits() {
    var u = GameData.units,
        i;
    for (i in u) {
        if (u[i].is_naval === undefined) {
            u[i].is_naval = (u[i].transport === null || u[i].transport);
        }
    }
}

function addslashes(str) {
    var result = str;
    result = result.replace(/\\/g, '\\\\');
    result = result.replace(/\'/g, '\\\'');
    result = result.replace(/\"/g, '\\"');
    result = result.replace(/\0/g, '\\0');
    return result;
}

function clearJQueryCache(tagType) {
    var i;
    for (i in jQuery.cache) {
        if (jQuery.cache[i] && jQuery.cache[i].handle && jQuery.cache[i].handle.elem) {
            if (jQuery.cache[i].handle.elem.tagName === tagType) {
                delete jQuery.cache[i];
            }
        } else if (isEmpty(jQuery.cache[i])) {
            delete jQuery.cache[i];
        }
    }
    return false;
}

function getHumanReadableTimeDate(time) {
    var hours = time.getUTCHours(),
        minutes = time.getUTCMinutes(),
        seconds = time.getUTCSeconds(),
        day = time.getUTCDate(),
        month = time.getUTCMonth() + 1,
        year = time.getUTCFullYear();
    if (hours < 10) {
        hours = '0' + hours;
    }
    if (minutes < 10) {
        minutes = '0' + minutes;
    }
    if (seconds < 10) {
        seconds = '0' + seconds;
    }
    if (day < 10) {
        day = '0' + day;
    }
    if (month < 10) {
        month = '0' + month;
    }
    return hours + ':' + minutes + ':' + seconds + ' ' + day + '/' + month + '/' + year;
}

function newDateByTimezone(year, month, day, hours, minutes, seconds, timezone_offset) {
    var date = new Date();
    date.setUTCFullYear(year, month, day);
    date.setUTCHours(hours, minutes, seconds - timezone_offset);
    return date;
}

function recalcCapacity(units, data, researches, pop, cap, progbar, slow_boats_needed, fast_boats_needed) {
    researches = researches || 0;
    data = data || GameData;
    var q, progress, total_capacity = 0,
        total_population = 0,
        pwidth = 460,
        berth = researches.berth || 0,
        gdunits = GameData.units,
        big_transporter_cap = gdunits.big_transporter.capacity,
        small_transporter_cap = gdunits.small_transporter.capacity;
    units.each(function () {
        var i = this.name,
            count = ~~this.value,
            cap;
        if (count && !GameData.units[i].flying) {
            if (GameData.units[i].is_naval) {
                total_capacity += ((cap = data.units[i].capacity) + (cap ? researches.berth || 0 : 0)) * count;
            } else {
                total_population += data.units[i].population * count;
            }
        }
    });
    q = total_population / total_capacity;
    progress = (q >= 1 || !total_capacity) ? 0 : (1 - q) * -pwidth;
    pop.text(total_population);
    cap.text(total_capacity);
    progbar.stop(true, true).animate({
        backgroundPosition: progress + 'px'
    });
    if (slow_boats_needed) {
        slow_boats_needed.text(Math.ceil(total_population / (big_transporter_cap + berth)));
    }
    if (fast_boats_needed) {
        fast_boats_needed.text(Math.ceil(total_population / (small_transporter_cap + berth)));
    }
    return {
        'capacity': total_capacity,
        'population': total_population
    };
}

function gpAward() {
    var hasFacebookUID = true;
    var that = this;
    this.publishOnFacebook = function (award_data) {
        GrepoFacebook.init(Game.fb_api_id, Game.fb_required_perms, Game.locale_lang, handle, handle);

        function handle() {
            Layout.ajaxGet('player_award', 'publish_on_facebook', {
                award_id: award_data.award_id,
                level: award_data.level,
                name: award_data.name
            }, false, function (data) {
                if (data.facebook_data != null) {
                    var message = data.facebook_data.facebook_message;
                    var attachment = data.facebook_data.facebook_feed_attachment;
                    var action_links = data.facebook_data.facebook_feed_action_links;
                    GrepoFacebook.game.publishStream(message, attachment, action_links);
                }
            });
        }
    };
}

function watch(object, property, handler) {
    if (!object.watch) {
        throw 'Object.watch() does not exist.'
    }
    if (typeof property !== 'string') {
        return;
    }
    if (!handler) {
        handler = function (id, oldval, newval) {
            debug(property + ': ' + oldval + ' -> ' + newval);
            return newval;
        };
    }
    object.watch(property, handler);
}

function extendLayoutWithCountdown() {
    var foo = Layout instanceof GPLayout ? GPLayout : GPLayoutB;
    foo.prototype.Countdown = (function () {
        var timer = null,
            interval = 250,
            queue = [];
        var csdiff = Timestamp.clientServerDiff();

        function start() {
            timer = window.setInterval(tick, interval);
        }

        function stop() {
            window.clearInterval(timer = null);
        }

        function tick() {
            var i = queue.length;
            while (i--) {
                var elm = queue[i][0],
                    until = queue[i][1],
                    up = queue[i][2];
                var diff = (up ? until + (Timestamp.client() - csdiff) : until - (Timestamp.client() - csdiff)),
                    seconds = Math.round(diff);
                if (elm && seconds >= 0) {
                    switch (up) {
                    case false:
                        elm.innerHTML = readableSeconds(Math.max(seconds, 0));
                        break;
                    case true:
                        elm.innerHTML = '~' + readableUnixTimestamp(seconds, 'player_timezone');
                        break;
                    }
                } else {
                    if (seconds < 0 && elm) {
                        $(elm).trigger('finish');
                    }
                    if (queue[i][3]) {
                        queue[i][3]();
                    }
                    queue.remove(i);
                    if (!queue.length) {
                        stop();
                    }
                }
            }
        }
        this.autoClean = function () {
            var i = queue.length,
                elem;
            while (i--) {
                elem = queue[i][0];
                while (elem !== document && (elem = elem.parentNode)) {
                    null;
                }
                if (elem !== document) {
                    queue.remove(i);
                }
            }
        };
        this.addElem = function (elm, ts, up, callback) {
            if (!timer) {
                start();
            }
            if (up) {
                var i = queue.length;
                while (i--) {
                    if (queue[i][0] == elm) {
                        return (queue[i][1] = ts);
                    }
                }
            }
            var array = [elm, ts, up];
            queue.push(callback ? array.push(callback) : array);
        };
        return this;
    }.call({}));
}
jQuery.fn.countdown = function (until, options) {
    options = options || {};
    if (!Layout.Countdown) {
        extendLayoutWithCountdown();
    }
    var f = options.callback && typeof options.callback == 'function' ? options.callback : null;
    this.each(function () {
        if (!until) {
            until = parseInt(this.innerHTML, 10);
        }
        Layout.Countdown.addElem(this, until, false, f);
    });
    return this;
};
jQuery.fn.updateTime = function (offset) {
    var length = this.length;
    if (!Layout.Countdown) {
        extendLayoutWithCountdown();
    }
    this.each(function () {
        if (length > 1 || !offset) {
            offset = parseInt(this.innerHTML, 10);
        }
        Layout.Countdown.addElem(this, offset, true);
    });
    return this;
};

function fastXyHash() {
    this.obj = {};
}
fastXyHash.prototype.setByXy = function (x, y, value) {
    if (this.obj[x] === undefined) {
        this.obj[x] = {};
    }
    this.obj[x][y] = value;
};
fastXyHash.prototype.getByXy = function (x, y) {
    if (this.obj[x] === undefined || this.obj[x][y] === undefined) {
        return undefined;
    }
    return this.obj[x][y];
};
fastXyHash.prototype.getObj = function () {
    return this.obj;
};
fastXyHash.prototype.clean = function () {
    delete this.obj;
    this.obj = {};
};
fastXyHash.prototype.each = function (fn) {
    for (var x in this.obj) {
        for (var y in this.obj[x]) {
            var tmp = fn(this.obj[x][y]);
            if (tmp !== undefined) {
                this.obj[x][y] = tmp;
            }
        }
    }
};
fastXyHash.prototype.remove = function (x, y) {
    delete this.obj[x][y];
};

var HumanMessage = {
    initialize: function () {
        HumanMessage.msgID = 'human_message';
        HumanMessage.msgOpacity = 1;
        $('body').append('<div id="' + HumanMessage.msgID + '" class="human_message"></div>');
        HumanMessage.msgObj = $('#' + HumanMessage.msgID);
    },
    error: function (message) {
        HumanMessage.display(message, 'human_message_error');
    },
    success: function (message) {
        HumanMessage.display(message, 'human_message_success');
    },
    display: function (msg, className) {
        if (!HumanMessage.msgObj) {
            HumanMessage.initialize();
        }
        HumanMessage.remove(null, true);
        HumanMessage.msgObj.attr('class', '');
        HumanMessage.msgObj.addClass('human_message');
        HumanMessage.msgObj.addClass(className);
        HumanMessage.msgObj.stop(true, true);
        clearTimeout(HumanMessage.t1);
        HumanMessage.mouseCoord = null;
        HumanMessage.msgObj.html(msg);
        clearTimeout(HumanMessage.iid);
        HumanMessage.iid = setTimeout(function () {
            var visibleMsgObj = $('#' + HumanMessage.msgID + ':visible');
            if (visibleMsgObj.length) {
                HumanMessage.onMessageDisappeared();
            }
        }, 2000);
        HumanMessage.msgObj.show().animate({
            'opacity': 1
        }, 200, HumanMessage.onMessageVisible);
    },
    onMessageVisible: function () {
        HumanMessage.t1 = setTimeout(HumanMessage.remove, 4000);
        HumanMessage.bindEvents();
    },
    bindEvents: function () {
        $(window).mousemove(HumanMessage.onMousemove).click(HumanMessage.remove).keypress(HumanMessage.remove);
    },
    onMousemove: function (event) {
        if (HumanMessage.mouseCoord === null) {
            HumanMessage.mouseCoord = {
                'x': event.clientX,
                'y': event.clientY
            };
        }
        var dx = event.clientX - HumanMessage.mouseCoord.x,
            dy = event.clientY - HumanMessage.mouseCoord.y;
        if (dx * dx + dy * dy > 30 * 30) {
            HumanMessage.remove();
        }
    },
    stop: function () {
        HumanMessage.msgObj.stop(true, true);
        clearTimeout(HumanMessage.t1);
        $(window).unbind('mousemove', HumanMessage.onMousemove).unbind('click', HumanMessage.remove).unbind('keypress', HumanMessage.remove);
    },
    remove: function (evt, immediately) {
        immediately = immediately || false;
        HumanMessage.stop();
        if (immediately) {
            HumanMessage.onMessageDisappeared();
        } else {
            clearTimeout(HumanMessage.iid);
            clearTimeout(HumanMessage.t1);
            HumanMessage.t1 = setTimeout(function () {
                HumanMessage.msgObj.animate({
                    'opacity': 0
                }, 500, HumanMessage.onMessageDisappeared);
            }, 100);
        }
    },
    onMessageDisappeared: function () {
        HumanMessage.msgObj.hide();
    }
};

jQuery.fn.extend({
    mousePopup: function (popup) {
        popup.bindTo(this);
        return this;
    }
});
var MousePopup = function () {
        this.initialize.apply(this, arguments);
    };
jQuery.extend(MousePopup.prototype, {
    popup_wrapper: null,
    initialize: function (xhtml, styles) {
        if ((typeof xhtml) == 'function') {
            this.xhtml = xhtml.call();
        } else {
            this.xhtml = xhtml;
        }
        this.styles = styles || {};
        this.popup_wrapper = document.getElementById('popup_div');
        var that = this;
        $(this.popup_wrapper).unbind().bind('mouseover', function (e) {
            that.handlerMove(e);
        });
    },
    position: function () {
        var p_x = this.cur_x,
            p_y = this.cur_y,
            o_x = 10,
            o_y = 10,
            s_x = $(document).scrollLeft(),
            s_y = $(document).scrollTop(),
            v_x = $(window).width(),
            v_y = $(window).height(),
            pp_x = $(this.popup_wrapper).width(),
            pp_y = $(this.popup_wrapper).height();
        if ((p_x + pp_x + o_x) > v_x) {
            p_x = p_x - pp_x - o_x + s_x;
        } else {
            p_x = p_x + o_x + s_x;
        }
        if ((p_y + pp_y + o_y) > v_y) {
            p_y = p_y - pp_y - o_y + s_y;
        } else {
            p_y = p_y + o_y + s_y;
        }
        var pos = [Math.max(p_x, s_x), Math.max(p_y, s_y)];
        return pos;
    },
    p_x: 0,
    p_y: 0,
    handlerMove: function (ev) {
        this.cur_x = ev.clientX;
        this.cur_y = ev.clientY;
        var p = this.popup_wrapper,
            pos = this.position();
        var x = this.p_x - pos[0],
            y = this.p_y - pos[1];
        if (((x > 0 ? x : -x) > 4 || (y > 0 ? y : -y) > 4) && jQuery.browser.msie) {
            p.style.left = pos[0] + 'px';
            p.style.top = pos[1] + 'px';
            this.p_x = pos[0];
            this.p_y = pos[1];
        } else {
            p.style.left = pos[0] + 'px';
            p.style.top = pos[1] + 'px';
        }
        var that = this;
    },
    handlerOver: function (ev) {
        this.showDiv();
        this.handlerMove(ev);
    },
    handlerOut: function () {
        if (jQuery.browser.msie) {
            this.onOutAnimationComplete();
        } else {
            $(this.popup_wrapper).stop(true, true).animate({
                'opacity': '0'
            }, {
                'duration': 250,
                'complete': this.onOutAnimationComplete.bind(this)
            });
        }
    },
    onOutAnimationComplete: function () {
        $('#popup_div').hide().clearQueue();
    },
    showDiv: function () {
        if (this.enabled === false) return;
        $('#popup_content').html(this.xhtml);
        var popup_div = $('#popup_div'),
            basic_styles = {
                'position': 'absolute',
                'display': 'block',
                'z-index': 5001
            },
            styles = jQuery.merge(basic_styles, this.position(), this.styles);
        if (!jQuery.browser.msie) {
            styles = jQuery.extend(styles, {
                'opacity': 1
            });
        }
        popup_div.css(styles);
        popup_div.stop(true);
    },
    bindTo: function (el) {
        el = $(el);
        el.unbind('.popup').bind({
            'mouseenter.popup': this.handlerOver.bind(this),
            'mousemove.popup': this.handlerMove.bind(this),
            'mouseleave.popup': this.handlerOut.bind(this)
        });
    },
    disable: function () {
        this.enabled = false;
    },
    enable: function () {
        this.enabled = true;
    }
});

function Slider(options) {
    var ffwd = false,
        that = this;
    this._elementMin = options.elementMin || null;
    this._elementMax = options.elementMax || null;
    this._elementDown = options.elementDown || null;
    this._elementUp = options.elementUp || null;
    this._elementInput = options.elementInput;
    this._elementSlider = options.elementSlider;
    this._orientation = options.orientation || 'horizontal';
    this._max_overwrite = options.max_overwrite || false;
    this._callback = options.callback ||
    function () {};
    this._onmousedown = options.onmousedown ||
    function () {};
    if (options.elementDownFast && options.elementUpFast) {
        this._elementDownFast = options.elementDownFast;
        this._elementUpFast = options.elementUpFast;
        ffwd = true;
    }
    this._elementSlider.mouseup(function () {
        if (that._onmousedown) {
            that._onmousedown();
        }
    });
    this._min = options.min || 0;
    this._max = options.max || 100;
    this._value = options.value || this._min;
    this._step = options.step || 1;
    try {
        this._elementSlider.slider({
            'animate': true,
            'orientation': this._orientation,
            'min': this._min,
            'max': this._max,
            'value': this._value,
            'step': this._step
        });
    } catch (e) {}
    var sliderChange = (function (event, ui) {
        this.setValue(ui.value);
        this._callback();
    }).bind(this);
    this._elementSlider.bind('slide', sliderChange);
    if (this._elementMin != null && this._elementMax != null) {
        this._elementMin.click((function () {
            this.setValue(this.getMin());
        }).bind(this));
        this._elementMax.click((function () {
            this.setValue(this.getMax());
        }).bind(this));
    }
    if (this._elementDown && this._elementUp) {
        this._elementDown.click((function () {
            this.setValue(this.getValue() - this._step);
            this._elementInput.change();
        }).bind(this));
        this._elementUp.click((function () {
            this.setValue(this.getValue() + this._step);
            this._elementInput.change();
        }).bind(this));
    }
    if (ffwd) {
        this._elementDownFast.click((function () {
            this.setValue(this.getValue() - 100 * this._step);
        }).bind(this));
        this._elementUpFast.click((function () {
            this.setValue(this.getValue() + 100 * this._step);
        }).bind(this));
    }
    this._elementInput.bind('change', {
        self: this
    }, function (e) {
        e.data.self.setValue(this.value);
    });
    this._elementInput.focus(function () {
        this.select();
    });
}
Slider.prototype = new jQuery();
Slider.prototype.getValue = function () {
    return parseFloat(this._elementInput.val(), 10);
};
Slider.prototype.setValue = function (value) {
    value = ~~this._step != this._step ? ~~ (value * 10) / 10 : parseInt(value, 10);
    if (isNaN(value)) {
        return;
    }
    if (!this._max_overwrite) {
        value = Math.max(this._min, value);
        value = Math.min(this._max, value);
    } else {
        this.setMax(Math.max(this._max, value));
        value = Math.max(this._min, value);
    }
    this._value = value;
    this._elementInput[0].value = value;
    this._elementSlider.slider("option", 'value', value);
    this._elementSlider.trigger('change');
};
Slider.prototype.setMin = function (min) {
    this._min = min;
    if (this._elementMin) {
        this._elementMin.text(min);
    }
    this._elementSlider.slider('option', 'min', min);
};
Slider.prototype.getMin = function (value) {
    return this._min;
};
Slider.prototype.setMax = function (max) {
    this._max = max;
    if (this._elementMax) {
        this._elementMax.text(max);
    }
    this._elementSlider.slider('option', 'max', max);
};
Slider.prototype.getMax = function () {
    return this._max;
};
Slider.prototype.disable = function (param) {
    if (typeof param !== 'boolean') {
        return;
    }
    this._elementSlider.slider('option', 'disabled', param);
};

function Dialog(options) {
    this.options = options;
    this.parent = null;
}
Dialog.prototype.close = function () {
    $('#confirm_dialog').hide();
    $('#confirm_dialog').appendTo(this.parent);
    this.parent = null;
    return false;
}
Dialog.prototype.open = function () {
    if (!$('#confirm_dialog')) {
        alert('Dialog template is missing!');
    }
    if (this.parent) {
        return;
    }
    this.parent = $('#confirm_dialog').parent();
    $('#confirm_dialog_title').text(this.options.title);
    $('#confirm_dialog_text').text(this.options.text);
    var button_yes = $($('#confirm_dialog a')[0]);
    button_yes.unbind('click');
    button_yes.click(this.options.button_yes.callback_function);
    button_yes.find('.middle').text(this.options.button_yes.title);
    var button_no = $($('#confirm_dialog a')[1]);
    button_no.unbind('click');
    button_no.click(this.options.button_no.callback_function);
    button_no.find('.middle').text(this.options.button_no.title);
    $('#confirm_dialog').show();
}

var EndlessScroll = {
    paginate: false,
    locked: false,
    page_locked: false,
    prev_scroll_direction: {},
    lock: function () {
        this.locked = true;
    },
    unlock: function () {
        this.locked = false;
    },
    isLocked: function () {
        return this.locked;
    },
    lockPage: function () {
        this.page_locked = true;
    },
    unlockPage: function () {
        this.page_locked = false;
    },
    isPageLocked: function () {
        return this.page_locked;
    },
    jumpToPage: function (args, es_pagination_id, parent_html_elem_id, update_inner) {
        EndlessScroll.lock();
        var page = $('#' + es_pagination_id + ' .es_page_input').val();
        if (page > 0 && (page * args.limit) < args.last_element + args.limit) {
            args.start_element = page > 0 ? (page - 1) * args.limit : args.start_element;
            args.refetch = update_inner ? 1 : 0;
            Layout.ajaxGet(args.controller, args.action, args, false, function (_data) {
                if (update_inner) {
                    var jump_data = {
                        page: page,
                        start_element: args.start_element,
                        last_element: args.last_element,
                        limit: args.limit,
                        first_element: args.first_element
                    },
                        ins_data = $(_data.view);
                    if (ins_data.hasClass('top')) {
                        ins_data.toggleClass('top bottom');
                    }
                    $('#' + args.insert_elem_id).html(ins_data);
                    $('#' + args.insert_elem_id).children().addClass('bottom');
                    $('#' + args.elem_id).scrollTop(0);
                    $(document).trigger('es_jump_' + args.elem_id, jump_data);
                    EndlessScroll.correctlastScrollPosition(args.elem_id, true, page);
                } else {
                    $('#' + parent_html_elem_id).html(_data.html);
                }
                EndlessScroll.prev_scroll_direction[args.elem_id] = 'none';
                if (typeof args.callback == 'function') {
                    args.callback(args, _data, true);
                }
                EndlessScroll.unlock();
            });
        }
    },
    updatePage: function (pagination_elem_id, current_page, was_at_top, visible_height, scrollPos, sum_top) {
        if (EndlessScroll.isPageLocked()) {
            return current_page;
        }
        EndlessScroll.lockPage();
        var page_top = sum_top - (visible_height / 2) > scrollPos,
            new_page;
        if (!page_top && was_at_top) {
            new_page = ~~current_page + 1;
            $('#' + pagination_elem_id + ' .es_page_input').val(new_page);
            return new_page;
        } else if (page_top && !was_at_top) {
            new_page = ~~current_page - 1;
            $('#' + pagination_elem_id + ' .es_page_input').val(new_page);
            return new_page;
        }
        return current_page;
    },
    correctlastScrollPosition: function (elem_id, is_at_top, current_page) {
        var data = {
            scroll_pos: $('#' + elem_id).scrollTop(),
            is_at_top: is_at_top,
            current_page: current_page
        };
        $(document).trigger('essl_trigger_' + elem_id, data);
    }
};

function GPEndlessScroll(args) {
    if (args.insert_elem_id == null) {
        args.insert_elem_id = args.elem_id;
    }
    if (!args.limit) {
        args.limit = 35;
    }
    if (!args.ttl) {
        args.ttl = 600;
    }
    if (!args.window_handle) {
        args.window_handle = Layout;
    }
    var ajaxloader = new gpAjax(args.window_handle, true),
        atBoF = typeof args.first_element == 'undefined' ? true : args.first_element <= args.limit,
        start_element = atBoF == false ? args.first_element - (args.first_element % args.limit) : 0,
        last_start_element = args.last_element - ((args.last_element % args.limit == 0) ? args.limit : args.last_element % args.limit),
        atEoF = last_start_element == start_element,
        EOF = atEoF == true ? true : false,
        BOF = atBoF == false ? false : true,
        new_start_element = 0,
        ajax_data, es_pagination_id = args.es_pagination_id == 'undefined' ? 'es_pagination' : args.es_pagination_id,
        fpen = args.first_element,
        elem = $('#' + es_pagination_id + ' .es_page_input');
    EndlessScroll.prev_scroll_direction[args.elem_id] = 'none';
    if (elem) {
        var start_page = Math.ceil((start_element + 1) / args.limit);
        elem.val(start_page);
    }
    $('#' + args.insert_elem_id).children().addClass('bottom');
    $(document).unbind('es_jump_' + args.elem_id);
    $(document).bind('es_jump_' + args.elem_id, function (evt, data) {
        atBoF = data.page == 1;
        start_element = data.start_element;
        last_start_element = data.last_element - (data.last_element % data.limit);
        atEoF = last_start_element == start_element;
        EOF = atEoF == true ? true : false;
        BOF = atBoF == false ? false : true;
        EndlessScroll.prev_scroll_direction[args.elem_id] = 'none';
        fpen = data.first_element;
    });
    var that = this,
        es_object = {
            insert_elem_id: args.insert_elem_id,
            pixelOffset: args.pixelOffset ? args.pixelOffset : 5,
            start_page: start_page,
            callback: function (isScrollDown) {
                EndlessScroll.lock();
                var ls_key, ls_store_key, scroll_direction = isScrollDown ? 'down' : 'up';
                if (isScrollDown) {
                    if (EOF == true) {
                        EndlessScroll.unlock();
                        return false;
                    }
                    new_start_element = start_element + (EndlessScroll.prev_scroll_direction[args.elem_id] != 'up' ? 1 : 2) * args.limit;
                    ls_store_key = 'es_' + args.elem_id + '_' + (EndlessScroll.prev_scroll_direction[args.elem_id] == 'up' ? start_element : (start_element - args.limit));
                } else {
                    if (BOF == true) {
                        EndlessScroll.unlock();
                        return false;
                    }
                    new_start_element = start_element - (EndlessScroll.prev_scroll_direction[args.elem_id] != 'down' ? 1 : 2) * args.limit;
                    ls_store_key = 'es_' + args.elem_id + '_' + (EndlessScroll.prev_scroll_direction[args.elem_id] == 'up' ? (start_element + args.limit) : start_element);
                }
                ls_key = 'es_' + args.elem_id + '_' + new_start_element;
                that.getData(ls_key, ls_store_key, new_start_element, scroll_direction);
                return {
                    visible_height: $('#' + args.elem_id).height(),
                    inner_elem_id: args.insert_elem_id,
                    pagination_elem_id: es_pagination_id
                };
            }
        };
    this.getData = function (ls_key, ls_store_key, new_start_element, scroll_direction) {
        LocalStore.get(ls_key, function (ok, data) {
            if (ok) {
                debug('got data from ls with key: ' + ls_key)
                if (data.length !== 0) {
                    var scroll_options = {
                        start_element: new_start_element,
                        elements_length: $(data).length,
                        view: data
                    };
                    scroll_direction == 'up' ? that.scrollUp(scroll_options, ls_store_key) : that.scrollDown(scroll_options, ls_store_key);
                    EndlessScroll.unlock();
                }
            } else {
                ajax_data = {
                    start_element: new_start_element,
                    limit: args.limit,
                    scroll_direction: 'top',
                    refetch: 1
                };
                if (typeof args.args != 'undefined' && args.args != null && args.args != '') {
                    ajax_data.es_args = args.args;
                }
                ajaxloader.tmpGet(args.controller, args.action, ajax_data, true, function (_layout, _data, _flag) {
                    data = _data;
                    if (data.length !== 0) {
                        scroll_direction == 'up' ? that.scrollUp(data, ls_store_key) : that.scrollDown(data, ls_store_key);
                    }
                    EndlessScroll.unlock();
                });
            }
        });
    };
    this.scrollDown = function (data, ls_key) {
        EOF = data.elements_length < args.limit || (data.start_element == last_start_element);
        start_element = data.start_element;
        var sum_lines_to_remove = $('#' + args.insert_elem_id + ' .top').length;
        if (sum_lines_to_remove > 0) {
            var sum_top_elements_height = that.getSumElementsHeight(args.insert_elem_id, '.top'),
                old_scroll_position = $('#' + args.elem_id).scrollTop(),
                elm = $('#' + args.insert_elem_id + ' .top').remove(),
                ls_data = elm.wrapAll('<table id="' + ls_key + '"><tbody></tbody></table>').parent().html();
            debug('store data into ls with key: ' + ls_key)
            LocalStore.set(ls_key, ls_data, args.ttl);
            $('#' + ls_key).remove();
            BOF = false;
        }
        $('#' + args.insert_elem_id + ' .bottom').toggleClass('bottom top');
        var ins_data = $(data.view);
        if (ins_data.hasClass('top')) {
            ins_data.toggleClass('top bottom');
        }
        $("#" + args.insert_elem_id).append(ins_data);
        $('#' + args.insert_elem_id).children().not('.top').addClass('bottom');
        $('.bottom').show();
        if (sum_lines_to_remove > 0) {
            $('#' + args.elem_id).scrollTop(that.scrollPos(true, sum_top_elements_height, old_scroll_position));
            EndlessScroll.correctlastScrollPosition(args.elem_id, true);
            fpen += args.limit;
        }
        EndlessScroll.prev_scroll_direction[args.elem_id] = 'down';
        if (typeof args.callback == 'function') {
            args.callback(args, data, true);
        }
    }
    this.scrollUp = function (data, ls_key) {
        start_element = data.start_element;
        if (data.start_element <= 0) {
            BOF = true;
        }
        var sum_bottom = $('#' + args.insert_elem_id + ' .bottom').length,
            sum_top = $('#' + args.insert_elem_id + ' .top').length;
        if (sum_bottom > 0 && sum_top > 0) {
            var elm = $('#' + args.insert_elem_id + ' .bottom').remove(),
                ls_data = elm.wrapAll('<table id="' + ls_key + '"><tbody></tbody></table>').parent().html();
            $('#' + ls_key).remove();
            debug('store data into ls with key: ' + ls_key)
            LocalStore.set(ls_key, ls_data, args.ttl);
            EOF = false;
        }
        var old_scroll_position = $('#' + args.insert_elem_id).scrollTop();
        $('#' + args.insert_elem_id + ' .top').toggleClass('bottom top');
        var ins_data = $(data.view);
        if (ins_data.hasClass('bottom')) {
            ins_data.toggleClass('top bottom');
        }
        $("#" + args.insert_elem_id).prepend(ins_data);
        $('#' + args.insert_elem_id).children().not('.bottom').addClass('top');
        $('.top').show();
        if (sum_bottom > 0) {
            var sum_top_elements_height = that.getSumElementsHeight(args.insert_elem_id, '.top');
            $('#' + args.elem_id).scrollTop(that.scrollPos(false, sum_top_elements_height, old_scroll_position));
            EndlessScroll.correctlastScrollPosition(args.elem_id, false);
            fpen = start_element;
        }
        EndlessScroll.prev_scroll_direction[args.elem_id] = 'up';
        if (typeof args.callback == 'function') {
            args.callback(args, data, false);
        }
    }
    this.scrollPos = function (isScrollDown, sum_top_elements_height, old_scroll_position) {
        return isScrollDown ? old_scroll_position - sum_top_elements_height : old_scroll_position + sum_top_elements_height;
    };
    this.getSumElementsHeight = function (insert_elem_id, childClass) {
        var sum_elm_height = 0;
        $('#' + insert_elem_id).children(childClass).each(function (key, elem) {
            sum_elm_height += $(elem).outerHeight();
        });
        return sum_elm_height;
    };
    $('#' + args.elem_id).endlessScroll(es_object);
}

Persist = (function () {
    var VERSION = '0.2.0',
        P, B, esc, init, empty, ec;
    ec = (function () {
        var EPOCH = 'Thu, 01-Jan-1970 00:00:01 GMT',
            RATIO = 1000 * 60 * 60 * 24,
            KEYS = ['expires', 'path', 'domain'],
            esc = escape,
            un = unescape,
            doc = document,
            me;
        var get_now = function () {
                var r = new Date();
                r.setTime(r.getTime());
                return r;
            }
        var cookify = function (c_key, c_val) {
                var i, key, val, r = [],
                    opt = (arguments.length > 2) ? arguments[2] : {};
                r.push(esc(c_key) + '=' + esc(c_val));
                for (i = 0; i < KEYS.length; i++) {
                    key = KEYS[i];
                    if (val = opt[key]) r.push(key + '=' + val);
                }
                if (opt.secure) r.push('secure');
                return r.join('; ');
            }
        var alive = function () {
                var k = '__EC_TEST__',
                    v = new Date();
                v = v.toGMTString();
                this.set(k, v);
                this.enabled = (this.remove(k) == v);
                return this.enabled;
            }
        me = {
            set: function (key, val) {
                var opt = (arguments.length > 2) ? arguments[2] : {},
                    now = get_now(),
                    expire_at, cfg = {};
                if (opt.expires) {
                    opt.expires *= RATIO;
                    cfg.expires = new Date(now.getTime() + opt.expires);
                    cfg.expires = cfg.expires.toGMTString();
                }
                var keys = ['path', 'domain', 'secure'];
                for (i = 0; i < keys.length; i++)
                if (opt[keys[i]]) cfg[keys[i]] = opt[keys[i]];
                var r = cookify(key, val, cfg);
                doc.cookie = r;
                return val;
            },
            has: function (key) {
                key = esc(key);
                var c = doc.cookie,
                    ofs = c.indexOf(key + '='),
                    len = ofs + key.length + 1,
                    sub = c.substring(0, key.length);
                return ((!ofs && key != sub) || ofs < 0) ? false : true;
            },
            get: function (key) {
                key = esc(key);
                var c = doc.cookie,
                    ofs = c.indexOf(key + '='),
                    len = ofs + key.length + 1,
                    sub = c.substring(0, key.length),
                    end;
                if ((!ofs && key != sub) || ofs < 0) return null;
                end = c.indexOf(';', len);
                if (end < 0) end = c.length;
                return un(c.substring(len, end));
            },
            remove: function (k) {
                var r = me.get(k),
                    opt = {
                        expires: EPOCH
                    };
                doc.cookie = cookify(k, '', opt);
                return r;
            },
            keys: function () {
                var c = doc.cookie,
                    ps = c.split('; '),
                    i, p, r = [];
                for (i = 0; i < ps.length; i++) {
                    p = ps[i].split('=');
                    r.push(un(p[0]));
                }
                return r;
            },
            all: function () {
                var c = doc.cookie,
                    ps = c.split('; '),
                    i, p, r = [];
                for (i = 0; i < ps.length; i++) {
                    p = ps[i].split('=');
                    r.push([un(p[0]), un(p[1])]);
                }
                return r;
            },
            version: '0.2.1',
            enabled: false
        };
        me.enabled = alive.call(me);
        return me;
    }());
    var index_of = (function () {
        if (Array.prototype.indexOf) return function (ary, val) {
            return Array.prototype.indexOf.call(ary, val);
        };
        else return function (ary, val) {
            var i, l;
            for (i = 0, l = ary.length; i < l; i++)
            if (ary[i] == val) return i;
            return -1;
        };
    })();
    empty = function () {};
    esc = function (str) {
        return 'PS' + str.replace(/_/g, '__').replace(/ /g, '_s');
    };
    C = {
        search_order: ['localstorage', 'whatwg_db', 'globalstorage'],
        name_re: /^[a-z][a-z0-9_ -]+$/i,
        methods: ['init', 'get', 'set', 'remove', 'vacuum', 'load', 'save'],
        sql_version: 3,
        sql: {
            version: this.sql_version,
            create: "CREATE TABLE IF NOT EXISTS gp_persist_data_" + this.sql_version + " (k TEXT UNIQUE NOT NULL PRIMARY KEY, v TEXT NOT NULL, t TEXT NOT NULL, vu INTEGER NOT NULL)",
            drop: "DROP TABLE IF EXISTS gp_persist_data_" + this.sql_version + "",
            get: "SELECT v, t, vu FROM gp_persist_data_" + this.sql_version + " WHERE k = ?",
            set: "INSERT INTO gp_persist_data_" + this.sql_version + "(k, v, t, vu) VALUES (?, ?, ?, ?)",
            remove: "DELETE FROM gp_persist_data_" + this.sql_version + " WHERE k = ?",
            vacuum: "DELETE FROM gp_persist_data_" + this.sql_version + " WHERE vu < ?"
        }
    };
    B = {
        gears: {
            size: -1,
            test: function () {
                return (window.google && window.google.gears) ? true : false;
            },
            methods: {
                transaction: function (fn) {
                    var db = this.db;
                    db.execute('BEGIN').close();
                    fn.call(this, db);
                    db.execute('COMMIT').close();
                },
                init: function () {
                    var db;
                    db = this.db = google.gears.factory.create('beta.database');
                    db.open(esc(this.name));
                    db.execute(C.sql.create).close();
                },
                clear: function () {
                    var db = this.db;
                    db.execute(C.sql.drop).close();
                    db.execute(C.sql.create).close();
                },
                get: function (key, fn, scope, userdata) {
                    var r, sql = C.sql.get;
                    if (!fn) return;
                    this.transaction(function (t) {
                        var is_valid, val, type;
                        r = t.execute(sql, [key]);
                        is_valid = r.isValidRow();
                        val = is_valid ? r.field(0) : null;
                        if (is_valid) {
                            if (r.field(2) != 0 && r.field(2) < ~~Timestamp.server()) {
                                this.remove(key);
                                fn.call(scope || this, false, null, userdata);
                                return;
                            }
                        }
                        r.close();
                        fn.call(scope || this, is_valid, $.parseJSON(val), userdata);
                    });
                },
                set: function (key, val, fn, ttl, scope, userdata) {
                    var rm_sql = C.sql.remove,
                        sql = C.sql.set,
                        r;
                    ttl = ~~ttl;
                    if (ttl != 0) ttl = ttl + ~~Timestamp.server();
                    this.transaction(function (t) {
                        t.execute(rm_sql, [key]).close();
                        t.execute(sql, [key, JSON.stringify(val), typeof val, ttl]).close();
                        if (fn) fn.call(scope || this, true, val, userdata);
                    });
                },
                remove: function (key, fn, scope, userdata) {
                    var get_sql = C.sql.get;
                    sql = C.sql.remove, r, val = null, is_valid = false;
                    this.transaction(function (t) {
                        if (fn) {
                            r = t.execute(get_sql, [key]);
                            is_valid = r.isValidRow();
                            val = is_valid ? r.field(0) : null;
                            r.close();
                        }
                        if (!fn || is_valid) {
                            t.execute(sql, [key]).close();
                        }
                        if (fn) fn.call(scope || this, is_valid, val, userdata);
                    });
                },
                vacuum: function () {
                    var vacuum_sql = C.sql.vacuum;
                    this.transaction(function (t) {
                        t.execute(vacuum_sql, [~~Timestamp.server()]).close();
                    });
                }
            }
        },
        whatwg_db: {
            size: 2048 * 1024,
            test: function () {
                var name = 'PersistJS Test',
                    desc = 'Persistent database test.';
                if (!window.openDatabase) return false;
                if (!window.openDatabase(name, C.sql.version, desc, B.whatwg_db.size)) return false;
                return true;
            },
            methods: {
                transaction: function (fn) {
                    if (!this.db_created) {
                        this.db.transaction(function (t) {
                            t.executeSql(C.sql.create, [], function () {
                                this.db_created = true;
                            });
                        }, empty);
                    }
                    this.db.transaction(fn);
                },
                init: function () {
                    this.db = openDatabase(this.name, C.sql.version, this.o.about || ("Persistent storage for " + this.name), this.o.size || B.whatwg_db.size);
                },
                clear: function () {
                    this.db.transaction(function (t) {
                        t.executeSql(C.sql.drop, [], function () {
                            this.db_created = false;
                        });
                    }, empty);
                },
                get: function (key, fn, scope, userdata) {
                    var sql = C.sql.get;
                    var val;
                    if (!fn) return;
                    scope = scope || this;
                    this.transaction(function (t) {
                        t.executeSql(sql, [key], function (t, r) {
                            if (r.rows.length > 0) {
                                if (r.rows.item(0)['vu'] != 0 && r.rows.item(0)['vu'] < ~~Timestamp.server()) {
                                    this.remove(key);
                                    fn.call(scope, false, null, userdata);
                                    return;
                                }
                                fn.call(scope, true, $.parseJSON(r.rows.item(0)['v']), userdata);
                            } else fn.call(scope, false, null, userdata);
                        });
                    });
                },
                set: function (key, val, fn, ttl, scope, userdata) {
                    var rm_sql = C.sql.remove,
                        sql = C.sql.set;
                    this.transaction(function (t) {
                        t.executeSql(rm_sql, [key], function () {
                            ttl = ~~ttl;
                            if (ttl != 0) ttl = ttl + ~~Timestamp.server();
                            t.executeSql(sql, [key, JSON.stringify(val), typeof val, ttl], function (t, r) {
                                if (fn) fn.call(scope || this, true, val, userdata);
                            });
                        });
                    });
                    return val;
                },
                remove: function (key, fn, scope, userdata) {
                    var get_sql = C.sql.get;
                    sql = C.sql.remove;
                    this.transaction(function (t) {
                        if (fn) {
                            t.executeSql(get_sql, [key], function (t, r) {
                                if (r.rows.length > 0) {
                                    var val = r.rows.item(0)['v'];
                                    t.executeSql(sql, [key], function (t, r) {
                                        fn.call(scope || this, true, val, userdata);
                                    });
                                } else {
                                    fn.call(scope || this, false, null, userdata);
                                }
                            });
                        } else {
                            t.executeSql(sql, [key]);
                        }
                    });
                },
                vacuum: function () {
                    var vacuum_sql = C.sql.vacuum;
                    this.transaction(function (t) {
                        t.executeSql(vacuum_sql, [~~Timestamp.server()]);
                    });
                }
            }
        },
        globalstorage: {
            size: 5 * 1024 * 1024,
            test: function () {
                return window.globalStorage ? true : false;
            },
            methods: {
                key: function (key) {
                    return esc(this.name) + esc(key);
                },
                metakey: function (key) {
                    return 'meta_' + esc(this.name) + esc(key);
                },
                init: function () {
                    this.store = globalStorage[this.o.domain];
                },
                clear: function () {
                    this.store.clear();
                },
                get: function (_key, fn, scope, userdata) {
                    var meta, metakey, key;
                    metakey = this.metakey(_key);
                    key = this.key(_key);
                    meta = $.parseJSON(this.store.getItem(metakey));
                    if (meta) {
                        if (meta.valid_until > 0 && meta.valid_until < ~~Timestamp.server()) {
                            this.remove(_key);
                            fn.call(scope || this, false, null, userdata);
                            return;
                        }
                        if (fn) fn.call(scope || this, true, $.parseJSON(this.store.getItem(key)), userdata);
                    } else {
                        if (fn) fn.call(scope || this, false, null, userdata);
                    }
                },
                set: function (key, val, fn, ttl, scope, userdata) {
                    var metakey = this.metakey(key);
                    key = this.key(key);
                    ttl = ~~ttl;
                    if (ttl != 0) ttl = ttl + ~~Timestamp.server();
                    try {
                        this.store.setItem(metakey, JSON.stringify({
                            type: (typeof val),
                            valid_until: ttl
                        }));
                        this.store.setItem(key, JSON.stringify(val));
                    } catch (e) {
                        if (fn) fn.call(scope || this, false, null, userdata);
                        return;
                    }
                    if (fn) fn.call(scope || this, true, val, userdata);
                },
                remove: function (key, fn, scope, userdata) {
                    var val, metakey, meta;
                    metakey = this.metakey(key);
                    key = this.key(key);
                    meta = this.store.getItem(metakey);
                    if (meta) {
                        if (meta.valid_until == 0 || meta.valid_until >= ~~Timestamp.server()) {
                            val = this.store.getItem(key);
                        } else val = null;
                    } else val = null;
                    this.store.removeItem(metakey);
                    this.store.removeItem(key);
                    if (fn) fn.call(scope || this, (val !== null), val, userdata);
                },
                vacuum: function () {
                    var ts = ~~Timestamp.server();
                    for (var i = 0; i < this.store.length; i++) {
                        var k = this.store.key(i);
                        if (typeof k == "string" && "meta_" == k.substr(0, 5)) {
                            var meta = $.parseJSON(this.store.getItem(k));
                            if (meta && meta.valid_until != 0 && meta.valid_until < ts) {
                                this.store.removeItem(k);
                                this.store.removeItem(k.substr(5));
                            }
                        }
                    }
                }
            }
        },
        localstorage: {
            size: -1,
            test: function () {
                return window.localStorage ? true : false;
            },
            methods: {
                key: function (key) {
                    return esc(this.name) + esc(key);
                },
                metakey: function (key) {
                    return 'meta_' + esc(this.name) + esc(key);
                },
                init: function () {
                    this.store = localStorage;
                },
                clear: function () {
                    this.store.clear();
                },
                get: function (_key, fn, scope, userdata) {
                    var meta, metakey, val, key, json;
                    metakey = this.metakey(_key);
                    key = this.key(_key);
                    meta = $.parseJSON(this.store.getItem(metakey));
                    if (meta) {
                        if (meta.valid_until > 0 && meta.valid_until < ~~Timestamp.server()) {
                            this.remove(_key);
                            fn.call(scope || this, false, null, userdata);
                            return;
                        }
                        if (fn) {
                            json = $.parseJSON(this.store.getItem(key));
                            fn.call(scope || this, true, json, userdata);
                        }
                    } else {
                        if (fn) {
                            fn.call(scope || this, false, null, userdata);
                        }
                    }
                },
                set: function (key, val, fn, ttl, scope, userdata) {
                    var metakey = this.metakey(key);
                    key = this.key(key);
                    ttl = ~~ttl;
                    if (ttl != 0) ttl = ttl + ~~Timestamp.server();
                    try {
                        this.store.setItem(metakey, JSON.stringify({
                            type: (typeof val),
                            valid_until: ttl
                        }));
                        this.store.setItem(key, JSON.stringify(val));
                    } catch (e) {
                        if (fn) fn.call(scope || this, false, null, userdata);
                        return;
                    }
                    if (fn) fn.call(scope || this, true, val, userdata);
                },
                remove: function (_key, fn, scope, userdata) {
                    var val, metakey, key, meta;
                    metakey = this.metakey(_key);
                    key = this.key(_key);
                    meta = this.store.getItem(metakey);
                    if (meta.valid_until == 0 || meta.valid_until >= ~~Timestamp.server()) val = this.store.getItem(key);
                    else val = null;
                    this.store.removeItem(key);
                    this.store.removeItem(metakey);
                    if (fn) fn.call(scope || this, (val !== null), val, userdata);
                },
                vacuum: function () {
                    var ts = ~~Timestamp.server();
                    for (var i = 0; i < this.store.length; i++) {
                        var k = this.store.key(i);
                        if (typeof k == "string" && "meta_" == k.substr(0, 5)) {
                            var meta = $.parseJSON(this.store.getItem(k));
                            if (meta && meta.valid_until != 0 && meta.valid_until < ts) {
                                this.store.removeItem(k);
                                this.store.removeItem(k.substr(5));
                            }
                        }
                    }
                }
            }
        }
    };
    var init = function () {
            var i, l, b, key, fns = C.methods,
                keys = C.search_order;
            for (i = 0, l = fns.length; i < l; i++)
            P.Store.prototype[fns[i]] = empty;
            P.type = null;
            P.size = -1;
            for (i = 0, l = keys.length; !P.type && i < l; i++) {
                b = B[keys[i]];
                if (b.test()) {
                    P.type = keys[i];
                    P.size = b.size;
                    for (key in b.methods)
                    P.Store.prototype[key] = b.methods[key];
                }
            }
            P._init = true;
        };
    P = {
        VERSION: VERSION,
        type: null,
        size: 0,
        add: function (o) {
            B[o.id] = o;
            C.search_order = [o.id].concat(C.search_order);
            init();
        },
        remove: function (id) {
            var ofs = index_of(C.search_order, id);
            if (ofs < 0) return;
            C.search_order.splice(ofs, 1);
            delete B[id];
            init();
        },
        Cookie: ec,
        isAvailable: function () {
            if (P.type) return true;
            return false;
        },
        getType: function () {
            return P.type;
        },
        Store: function (name, o) {
            if (!C.name_re.exec(name)) throw new Error("Invalid name");
            if (!P.type) throw new Error("No suitable storage found");
            o = o || {};
            this.name = name;
            o.domain = o.domain || location.host || 'localhost';
            o.domain = o.domain.replace(/:\d+$/, '')
            this.o = o;
            o.expires = o.expires || 365 * 2;
            o.path = o.path || '/';
            this.init();
        }
    };
    init();
    return P;
})();

var GrepoFacebook = function grepoFacebook() {
        var that = this;
        var perms = null;
        var callback = null;
        var callback_nosession = null;
        var required_permissions = '';
        var ajaxloader = new gpAjax(null, false);
        var initialized = false;
        var default_request_parameter = {};
        this.init = function (api_id, required_perms, locale_lang, callback_function, callback_function_nosession) {
            switch (locale_lang) {
            case 'en_DK':
                locale_lang = 'en_GB';
                break;
            }
            if (!initialized) {
                window.fbAsyncInit = function () {
                    FB.init({
                        appId: api_id,
                        status: true,
                        cookie: true,
                        xfbml: true
                    });
                    initialized = true;
                    callback = callback_function;
                    callback_nosession = callback_function_nosession;
                    FB.getLoginStatus(handleSessionResponse);
                };
                required_permissions = required_perms;
                var e = document.createElement('div');
                e.id = 'fb-root';
                document.body.appendChild(e);
                e = document.createElement('script');
                e.async = true;
                e.src = document.location.protocol + '//connect.facebook.net/' + locale_lang + '/all.js';
                document.getElementById('fb-root').appendChild(e);
            } else {
                callback = callback_function;
                callback_nosession = callback_function_nosession;
                FB.getLoginStatus(handleSessionResponse);
            }
        };

        function handleSessionResponse(response) {
            if (!response.session) {
                if (callback_nosession) {
                    callback_nosession();
                    callback_nosession = null;
                }
                return;
            }
            if (response.perms) {
                perms = response.perms;
            }
            if (callback) {
                callback();
                callback = null;
            }
        }
        this.ensureValidSession = function (callback_function) {
            callback = callback_function;
            if (!FB) {
                return;
            }
            FB.getLoginStatus(handleSessionResponse);
            if (!FB.getSession() || !perms) {
                FB.login(handleSessionResponse, {
                    perms: required_permissions
                });
            }
        };
        this.master = function Master(that) {
            this.setDefaultRequestParameters = function (parameters) {
                default_request_parameter = parameters;
            };
            this.get_serialized_session = function () {
                return {
                    session: FB.getSession()
                };
            };
            this.facebook_login_register = function () {
                that.ensureValidSession(lookUpPlayerAndLoginRegister);
            };
            this.get_worlds = function () {
                if (FB && FB.getSession() && perms) {
                    lookUpWorlds();
                }
            };

            function lookUpPlayerAndLoginRegister() {
                ajaxloader.post('/start/index?action=look_up_and_login_facebook_player', null, $.extend({
                    session: FB.getSession()
                }, default_request_parameter), false, function (_layout, _data, _flag) {
                    switch (_data.data) {
                    case 'fb_no_session':
                    case 'fb_no_valid_session':
                        break;
                    case 'no_account':
                        $.getJSON('start/register', $.extend({
                            'action': 'facebook_dialog'
                        }, default_request_parameter), function (data) {
                            showRegisterFacebookDialog(data.result);
                        });
                        break;
                    default:
                        Login.worlds = _data;
                        Login.showWorlds(true);
                        break;
                    }
                }, {}, null, false, false);
            }

            function lookUpWorlds() {
                ajaxloader.post('/start/index?action=look_up_and_login_facebook_player', null, $.extend({
                    session: FB.getSession()
                }, default_request_parameter), false, function (_layout, _data, _flag) {
                    if (_data.data == 'fb_no_session' || _data.data == 'fb_no_valid_session' || _data.data == 'no_account') {
                        return;
                    }
                    Login.worlds = _data;
                    Login.showWorlds(true);
                }, {}, null, false, false);
            }
            this.logOut = function () {
                if (FB && FB.getSession() && perms) {
                    FB.logout(function (response) {});
                }
            };

            function showRegisterFacebookDialog(content) {
                var facebook_dialog = $('#facebook_dialog_connect');
                facebook_dialog.dialog({
                    autoOpen: false,
                    height: 'auto',
                    width: 450,
                    resizable: false,
                    draggable: false,
                    modal: true
                });
                facebook_dialog.html(content).dialog({
                    autoOpen: true,
                    dialogClass: 'fb_dialog'
                });
            }
            return this;
        }.call(this.master, this);
        this.game = function Game(that) {
            this.facebook_connect_account = function () {
                that.ensureValidSession(connectWithAccount);
            };
            this.login_into_facebook = function () {
                that.ensureValidSession(gameLoginIntoFacebook);
            };
            this.publishStream = function (message, attachment, action_links) {
                that.ensureValidSession(function () {
                    publishStream(message, attachment, action_links);
                });
            };

            function connectWithAccount() {
                query('connect_with_facebook');
            }

            function gameLoginIntoFacebook() {
                query('facebook_connect');
            }

            function query(action) {
                ajaxloader.post('facebook_connect', action, {
                    session: FB.getSession()
                }, false, function (_layout, _data, _flag) {
                    var wnd = GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_PLAYER_SETTINGS);
                    if (wnd) {
                        wnd.getHandler().onRcvData(_data);
                    }
                }, {}, null, false, false);
            }

            function publishStream(message, attachment, action_links) {
                FB.ui({
                    method: 'feed',
                    picture: attachment.media[0].src,
                    message: message,
                    link: attachment.href,
                    name: attachment.name,
                    caption: attachment.caption,
                    description: attachment.description,
                    actions: {
                        name: action_links[0].text,
                        link: action_links[0].href
                    }
                }, function (response) {
                    if (response && response.post_id) {
                        HumanMessage.success('Your post has been published.');
                    } else {
                        HumanMessage.error('Your post has not been published.');
                    }
                });
            }
            return this;
        }.call(this.game, this);
        return this;
    }.call({});

function KtParameterException(message) {
    this.name = "KtParameterException";
    this.message = (message) ? message : "";
}
KtParameterException.prototype = new Error();

function Kontagent(apiKey, secretKey, useTestServer, validateParams, log_active) {
    this._baseApiUrl = "http://api.geo.kontagent.net/api/v1/";
    this._baseTestServerUrl = "http://test-server.kontagent.net/api/v1/";
    this._apiKey = apiKey;
    this._secretKey = secretKey;
    this._useTestServer = (useTestServer) ? useTestServer : false;
    this._validateParams = (validateParams) ? validateParams : true;
    this._log_active = log_active;
}
Kontagent.prototype._sendMessageViaImgTag = function (messageType, params, callback) {
    if (this._log_active == true) {
        if (this._validateParams == true) {
            var result;
            for (var paramKey in params) {
                result = KtValidator.validateParameter(messageType, paramKey, params[paramKey]);
                if (result != true) {
                    throw new KtParameterException(result);
                }
            }
        }
        var img = new Image();
        img.onerror = callback;
        img.onload = callback;
        if (this._useTestServer == true) {
            img.src = this._baseTestServerUrl + this._apiKey + "/" + messageType + "/?" + this._httpBuildQuery(params);
        } else {
            img.src = this._baseApiUrl + this._apiKey + "/" + messageType + "/?" + this._httpBuildQuery(params);
        }
    }
}
Kontagent.prototype._httpBuildQuery = function (data) {
    var query, key, val;
    var tmpArray = [];
    for (key in data) {
        val = encodeURIComponent(decodeURIComponent(data[key].toString()));
        key = encodeURIComponent(decodeURIComponent(key));
        tmpArray.push(key + "=" + val);
    }
    return tmpArray.join("&");
}
Kontagent.prototype._s4 = function () {
    return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
}
Kontagent.prototype.genUniqueTrackingTag = function () {
    var uniqueTrackingTag = "";
    for (i = 0; i < 4; i++) {
        uniqueTrackingTag += this._s4();
    }
    return uniqueTrackingTag;
}
Kontagent.prototype.genShortUniqueTrackingTag = function () {
    var shortUniqueTrackingTag = "";
    for (i = 0; i < 2; i++) {
        shortUniqueTrackingTag += this._s4();
    }
    return shortUniqueTrackingTag;
}
Kontagent.prototype.trackInviteSent = function (params, callback) {
    var apiParams = {
        s: params.userId,
        r: params.recipientUserIds,
        u: params.uniqueTrackingTag
    };
    if (params.subtype1) {
        apiParams.st1 = params.subtype1;
    }
    if (params.subtype2) {
        apiParams.st2 = params.subtype2;
    }
    if (params.subtype3) {
        apiParams.st3 = params.subtype3;
    }
    this._sendMessageViaImgTag("ins", apiParams, callback);
}
Kontagent.prototype.trackInviteResponse = function (params, callback) {
    var apiParams = {
        i: (params.appIsInstalled) ? 1 : 0,
        u: params.uniqueTrackingTag
    };
    if (params.recipientUserId) {
        apiParams.r = params.recipientUserId;
    }
    if (params.subtype1) {
        apiParams.st1 = params.subtype1;
    }
    if (params.subtype2) {
        apiParams.st2 = params.subtype2;
    }
    if (params.subtype3) {
        apiParams.st3 = params.subtype3;
    }
    this._sendMessageViaImgTag("inr", apiParams, callback);
}
Kontagent.prototype.trackNotificationSent = function (params, callback) {
    var apiParams = {
        s: params.userId,
        r: params.recipientUserIds,
        u: params.uniqueTrackingTag
    };
    if (params.subtype1) {
        apiParams.st1 = params.subtype1;
    }
    if (params.subtype2) {
        apiParams.st2 = params.subtype2;
    }
    if (params.subtype3) {
        apiParams.st3 = params.subtype3;
    }
    this._sendMessageViaImgTag("nts", apiParams, callback);
}
Kontagent.prototype.trackNotificationResponse = function (params, callback) {
    var apiParams = {
        i: (params.appIsInstalled) ? 1 : 0,
        u: params.uniqueTrackingTag
    };
    if (params.recipientUserId) {
        apiParams.r = params.recipientUserId;
    }
    if (params.subtype1) {
        apiParams.st1 = params.subtype1;
    }
    if (params.subtype2) {
        apiParams.st2 = params.subtype2;
    }
    if (params.subtype3) {
        apiParams.st3 = params.subtype3;
    }
    this._sendMessageViaImgTag("ntr", apiParams, callback);
}
Kontagent.prototype.trackNotificationEmailSent = function (params, callback) {
    var apiParams = {
        s: params.userId,
        r: params.recipientUserIds,
        u: params.uniqueTrackingTag
    };
    if (params.subtype1) {
        apiParams.st1 = params.subtype1;
    }
    if (params.subtype2) {
        apiParams.st2 = params.subtype2;
    }
    if (params.subtype3) {
        apiParams.st3 = params.subtype3;
    }
    this._sendMessageViaImgTag("nes", apiParams, callback);
}
Kontagent.prototype.trackNotificationEmailResponse = function (params, callback) {
    var apiParams = {
        i: (params.appIsInstalled) ? 1 : 0,
        u: params.uniqueTrackingTag
    };
    if (params.recipientUserId) {
        apiParams.r = params.recipientUserId;
    }
    if (params.subtype1) {
        apiParams.st1 = params.subtype1;
    }
    if (params.subtype2) {
        apiParams.st2 = params.subtype2;
    }
    if (params.subtype3) {
        apiParams.st3 = params.subtype3;
    }
    this._sendMessageViaImgTag("nei", apiParams, callback);
}
Kontagent.prototype.trackStreamPost = function (params, callback) {
    var apiParams = {
        s: params.userId,
        u: params.uniqueTrackingTag,
        tu: params.type
    };
    if (params.subtype1) {
        apiParams.st1 = params.subtype1;
    }
    if (params.subtype2) {
        apiParams.st2 = params.subtype2;
    }
    if (params.subtype3) {
        apiParams.st3 = params.subtype3;
    }
    this._sendMessageViaImgTag("pst", apiParams, callback);
}
Kontagent.prototype.trackStreamPostResponse = function (params, callback) {
    var apiParams = {
        i: (params.appIsInstalled) ? 1 : 0,
        u: params.uniqueTrackingTag,
        tu: params.type
    };
    if (params.recipientUserId) {
        apiParams.r = params.recipientUserId;
    }
    if (params.subtype1) {
        apiParams.st1 = params.subtype1;
    }
    if (params.subtype2) {
        apiParams.st2 = params.subtype2;
    }
    if (params.subtype3) {
        apiParams.st3 = params.subtype3;
    }
    this._sendMessageViaImgTag("psr", apiParams, callback);
}
Kontagent.prototype.trackEvent = function (params, callback) {
    var apiParams = {
        s: params.userId,
        n: params.eventName
    };
    if (params.value) {
        apiParams.v = params.value;
    }
    if (params.level) {
        apiParams.l = params.level;
    }
    if (params.subtype1) {
        apiParams.st1 = params.subtype1;
    }
    if (params.subtype2) {
        apiParams.st2 = params.subtype2;
    }
    if (params.subtype3) {
        apiParams.st3 = params.subtype3;
    }
    this._sendMessageViaImgTag("evt", apiParams, callback);
}
Kontagent.prototype.trackApplicationAdded = function (params, callback) {
    var apiParams = {
        s: params.userId
    };
    if (params.uniqueTrackingTag) {
        apiParams.u = params.uniqueTrackingTag;
    }
    if (params.shortUniqueTrackingTag) {
        apiParams.su = params.shortUniqueTrackingTag;
    }
    this._sendMessageViaImgTag("apa", apiParams, callback);
}
Kontagent.prototype.trackApplicationRemoved = function (params, callback) {
    var apiParams = {
        s: params.userId
    };
    this._sendMessageViaImgTag("apr", apiParams, callback);
}
Kontagent.prototype.trackThirdPartyCommClick = function (params, callback) {
    var apiParams = {
        i: (params.appIsInstalled) ? 1 : 0,
        tu: params.type,
        su: params.shortUniqueTrackingTag
    };
    if (params.userId) {
        apiParams.s = params.userId;
    }
    if (params.subtype1) {
        apiParams.st1 = params.subtype1;
    }
    if (params.subtype2) {
        apiParams.st2 = params.subtype2;
    }
    if (params.subtype3) {
        apiParams.st3 = params.subtype3;
    }
    this._sendMessageViaImgTag("ucc", apiParams, callback);
}
Kontagent.prototype.trackPageRequest = function (params, callback) {
    var apiParams = {
        s: params.userId,
        ts: Math.round(new Date().getTime() / 1000)
    };
    if (params.ipAddress) {
        apiParams.ip = params.ipAddress;
    }
    if (params.pageAddress) {
        apiParams.u = params.pageAddress;
    }
    this._sendMessageViaImgTag("pgr", apiParams, callback);
}
Kontagent.prototype.trackUserInformation = function (params, callback) {
    var apiParams = {
        s: params.userId
    };
    if (params.birthYear) {
        apiParams.b = params.birthYear;
    }
    if (params.gender) {
        apiParams.g = params.gender;
    }
    if (params.country) {
        apiParams.lc = params.country;
    }
    if (params.friendCount) {
        apiParams.f = params.friendCount;
    }
    this._sendMessageViaImgTag("cpu", apiParams, callback);
}
Kontagent.prototype.trackGoalCount = function (params, callback) {
    var apiParams = {
        s: params.userId
    };
    if (params.goalCount1) {
        apiParams.gc1 = params.goalCount1;
    }
    if (params.goalCount2) {
        apiParams.gc2 = params.goalCount2;
    }
    if (params.goalCount3) {
        apiParams.gc3 = params.goalCount3;
    }
    if (params.goalCount4) {
        apiParams.gc4 = params.goalCount4;
    }
    this._sendMessageViaImgTag("gci", apiParams, callback);
}
Kontagent.prototype.trackRevenue = function (params, callback) {
    var apiParams = {
        s: params.userId,
        v: params.value
    };
    if (params.type) {
        apiParams.tu = params.type;
    }
    if (params.subtype1) {
        apiParams.st1 = params.subtype1;
    }
    if (params.subtype2) {
        apiParams.st2 = params.subtype2;
    }
    if (params.subtype3) {
        apiParams.st3 = params.subtype3;
    }
    this._sendMessageViaImgTag("mtu", apiParams, callback);
}

function KtValidator() {}
KtValidator.validateParameter = function (messageType, paramName, paramValue) {
    return KtValidator['_validate' + KtValidator._upperCaseFirst(paramName)](messageType, paramName, paramValue);
}
KtValidator._upperCaseFirst = function (stringVal) {
    return stringVal.charAt(0).toUpperCase() + stringVal.slice(1);
}
KtValidator._validateB = function (messageType, paramName, paramValue) {
    if (typeof paramValue == "undefined" || paramValue != parseInt(paramValue) || paramValue < 1900 || paramValue > 2011) {
        return 'Invalid birth year.';
    } else {
        return true;
    }
}
KtValidator._validateF = function (messageType, paramName, paramValue) {
    if (typeof paramValue == "undefined" || paramValue != parseInt(paramValue) || paramValue < 0) {
        return 'Invalid friend count.'
    } else {
        return true;
    }
}
KtValidator._validateG = function (messageType, paramName, paramValue) {
    var regex = /^[mfu]$/;
    if (typeof paramValue == "undefined" || !regex.test(paramValue)) {
        return 'Invalid gender.';
    } else {
        return true;
    }
}
KtValidator._validateGc1 = function (messageType, paramName, paramValue) {
    if (typeof paramValue == "undefined" || paramValue != parseInt(paramValue) || paramValue < -16384 || paramValue > 16384) {
        return 'Invalid goal count value.';
    } else {
        return true;
    }
}
KtValidator._validateGc2 = function (messageType, paramName, paramValue) {
    return KtValidator._validateGc1(messageType, paramName, paramValue);
}
KtValidator._validateGc3 = function (messageType, paramName, paramValue) {
    return KtValidator._validateGc1(messageType, paramName, paramValue);
}
KtValidator._validateGc4 = function (messageType, paramName, paramValue) {
    return KtValidator._validateGc1(messageType, paramName, paramValue);
}
KtValidator._validateI = function (messageType, paramName, paramValue) {
    var regex = /^[01]$/;
    if (typeof paramValue == "undefined" || !regex.test(paramValue)) {
        return 'Invalid isAppInstalled value.';
    } else {
        return true;
    }
}
KtValidator._validateIp = function (messageType, paramName, paramValue) {
    var regex = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(\.\d{1,3})?$/;
    if (typeof paramValue == "undefined" || !regex.test(paramValue)) {
        return 'Invalid IP address value.';
    } else {
        return true;
    }
}
KtValidator._validateL = function (messageType, paramName, paramValue) {
    if (typeof paramValue == "undefined" || paramValue != parseInt(paramValue) || paramValue < 0) {
        return 'Invalid level value.';
    } else {
        return true;
    }
}
KtValidator._validateLc = function (messageType, paramName, paramValue) {
    var regex = /^[A-Z]{2}$/;
    if (typeof paramValue == "undefined" || !regex.test(paramValue)) {
        return 'Invalid country value.';
    } else {
        return true;
    }
}
KtValidator._validateLp = function (messageType, paramName, paramValue) {
    return true;
}
KtValidator._validateLs = function (messageType, paramName, paramValue) {
    return true;
}
KtValidator._validateN = function (messageType, paramName, paramValue) {
    var regex = /^[A-Za-z0-9-_]{1,32}$/;
    if (typeof paramValue == "undefined" || !regex.test(paramValue)) {
        return 'Invalid event name value.';
    } else {
        return true;
    }
}
KtValidator._validateR = function (messageType, paramName, paramValue) {
    if (messageType == 'ins' || messageType == 'nes' || messageType == 'nts') {
        var regex = /^[0-9]+(,[0-9]+)*$/;
        if (typeof paramValue == "undefined" || !regex.test(paramValue)) {
            return 'Invalid recipient user ids.';
        }
    } else if (messageType == 'inr' || messageType == 'psr' || messageType == 'nei' || messageType == 'ntr') {
        if (typeof paramValue == "undefined" || paramValue != parseInt(paramValue)) {
            return 'Invalid recipient user id.';
        }
    }
    return true;
}
KtValidator._validateS = function (messageType, paramName, paramValue) {
    if (typeof paramValue == "undefined" || paramValue != parseInt(paramValue)) {
        return 'Invalid user id.';
    } else {
        return true;
    }
}
KtValidator._validateSt1 = function (messageType, paramName, paramValue) {
    var regex = /^[A-Za-z0-9-_]{1,32}$/;
    if (typeof paramValue == "undefined" || !regex.test(paramValue)) {
        return 'Invalid subtype value.';
    } else {
        return true;
    }
}
KtValidator._validateSt2 = function (messageType, paramName, paramValue) {
    return KtValidator._validateSt1(messageType, paramName, paramValue);
}
KtValidator._validateSt3 = function (messageType, paramName, paramValue) {
    return KtValidator._validateSt1(messageType, paramName, paramValue);
}
KtValidator._validateSu = function (messageType, paramName, paramValue) {
    var regex = /^[A-Fa-f0-9]{8}$/;
    if (typeof paramValue == "undefined" || !regex.test(paramValue)) {
        return 'Invalid short unique tracking tag.';
    } else {
        return true;
    }
}
KtValidator._validateTs = function (messageType, paramName, paramValue) {
    if (typeof paramValue == "undefined" || paramValue != parseInt(paramValue)) {
        return 'Invalid timestamp.';
    } else {
        return true;
    }
}
KtValidator._validateTu = function (messageType, paramName, paramValue) {
    var regex;
    if (messageType == 'mtu') {
        regex = /^(direct|indirect|advertisement|credits|other)$/;
        if (typeof paramValue == "undefined" || !regex.test(paramValue)) {
            return 'Invalid monetization type.';
        }
    } else if (messageType == 'pst' || messageType == 'psr') {
        regex = /^(feedpub|stream|feedstory|multifeedstory|dashboard_activity|dashboard_globalnews)$/;
        if (typeof paramValue == "undefined" || !regex.test(paramValue)) {
            return 'Invalid stream post/response type.';
        }
    } else if (messageType == 'ucc') {
        regex = /^(ad|partner)$/;
        if (typeof paramValue == "undefined" || !regex.test(paramValue)) {
            return 'Invalid third party communication click type.';
        }
    }
    return true;
}
KtValidator._validateU = function (messageType, paramName, paramValue) {
    if (messageType != 'pgr') {
        var regex = /^[A-Fa-f0-9]{16}$/;
        if (typeof paramValue == "undefined" || !regex.test(paramValue)) {
            return 'Invalid unique tracking tag.';
        }
    }
    return true;
}
KtValidator._validateV = function (messageType, paramName, paramValue) {
    if (typeof paramValue == "undefined" || paramValue != parseInt(paramValue)) {
        return 'Invalid value.';
    } else {
        return true;
    }
}

function WndHandlerDefault(wndhandle) {
    WndHandlerDefault.prototype.wnd = wndhandle;
    var that = this;
    this.getDefaultWindowOptions = function () {
        var ret = {
            position: ['center', 'center'],
            height: 300,
            width: 460,
            resizable: false,
            autoresize: false,
            minimizable: true,
            title: 'Untitled Window'
        };
        return ret;
    };
    this.onInit = function (title, UIopts) {
        return true;
    };
    this.onFinal = function () {};
    this.onClose = function () {
        return true;
    };
    this.onFocus = function () {};
    this.onSetContent = function (html) {
        return html;
    };
    this.onRcvData = function (data) {};
    this.onRcvDataError = function (error_msg) {
        HumanMessage.error(error_msg);
    };
    this.onMessage = function () {
        var args = Array.prototype.slice.call(arguments);
        var func = args.shift();
        if (typeof this[func] !== 'function') {
            throw "function does not exist:" + func;
        }
        return this[func].apply(this, (args));
    };
}

function AbstractWndHandlerEmailValidation(wndhandle) {
    this.wnd = wndhandle;
}
AbstractWndHandlerEmailValidation.inherits(WndHandlerDefault);
AbstractWndHandlerEmailValidation.prototype.resendValidationEmail = function () {
    this.wnd.requestContentPost('player', 'resend_validation_email', {
        validate: 1
    });
};
AbstractWndHandlerEmailValidation.prototype.changeEmail = function () {
    var root = this.wnd.getJQElement().find('#change_email_form');
    var params = {};
    params.email = root.find('[name=email]').val();
    params.password = root.find('[name=password]').val();
    this.wnd.requestContentPost('player', 'change_email', params);
};
AbstractWndHandlerEmailValidation.prototype.validateEmail = function () {
    var that = this;
    var params = {};
    params.code = that.wnd.getJQElement().find('#validate_form [name=code]').val();
    this.wnd.ajaxRequestPost('player', 'validate_email', params, function (window, data) {
        $('#emailbar').remove();
        that.wnd.close();
    });
};

WndHandlerAttack = function () {
    this.unitInputs = null;
    this.data = null;
    this.same_island = null;
};
WndHandlerAttack.inherits(WndHandlerDefault);
WndHandlerAttack.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        height: 400,
        width: 500,
        resizable: false,
        title: 'Untitled Window'
    };
    return ret;
};
WndHandlerAttack.prototype.onClose = function () {
    this.wnd.getJQElement().find('a.select_all_units').unbind('click');
    this.wnd.getJQElement().find('a.index_unit').unbind('click');
    this.getUnitInputs().unbind('keyup change');
    return true;
};
WndHandlerAttack.prototype.bindCapacityCounter = function () {
    var that = this,
        max = this.root.find('span.capacity_max'),
        cur = this.root.find('span.capacity_current'),
        slow_boats_needed = this.root.find('span.slow_boats_needed'),
        fast_boats_needed = this.root.find('span.fast_boats_needed'),
        progbar = this.root.find('div.progress'),
        units = this.getUnitInputs();
    recalcCapacity(units, this.data, that.data.researches, cur, max, progbar, slow_boats_needed, fast_boats_needed);
    this.wnd.getJQElement().find('a.select_all_units').bind('click', function () {
        recalcCapacity(units, that.data, that.data.researches, cur, max, progbar, slow_boats_needed, fast_boats_needed);
    });
    this.wnd.getJQElement().find('a.index_unit').bind('click', function () {
        recalcCapacity(units, that.data, that.data.researches, cur, max, progbar, slow_boats_needed, fast_boats_needed);
    });
    this.getUnitInputs().bind('keyup change', function () {
        recalcCapacity(units, that.data, that.data.researches, cur, max, progbar, slow_boats_needed, fast_boats_needed);
    });
};
WndHandlerAttack.prototype.bindBootyCounter = function () {
    var that = this;
    var elm = {};
    elm.root = that.wnd.getJQElement();
    elm.container = elm.root.find('div.duration_container');
    elm.max_booty = elm.root.find('span.max_booty');

    function recalcBooty() {
        var total_capacity = 0;
        var u = GameData.units;
        var booty = true;
        that.getUnitInputs().each(function () {
            var i = this.name,
                count = ~~this.value;
            booty = u[i].hasOwnProperty('booty');
            if (count && booty) {
                total_capacity += u[i].booty * count;
            }
        });
        elm.max_booty.text('~' + total_capacity).show();
    }
    recalcBooty();
    elm.root.find('a.select_all_units').bind('click', recalcBooty);
    elm.root.find('a.index_unit').bind('click', recalcBooty);
    this.getUnitInputs().bind('keyup change', recalcBooty);
};
WndHandlerAttack.prototype.getUnitInputs = function () {
    return (this.unitInputs || (this.unitInputs = this.wnd.getJQElement().find('input.unit_input')));
};
WndHandlerAttack.prototype.resetUnitInputs = function () {
    this.getUnitInputs().each(function () {
        this.value = '';
    });
};
WndHandlerAttack.prototype.selectAllUnits = function () {
    var units = ITowns.getTown(Game.townId).units();
    this.getUnitInputs().each(function () {
        this.value = units[this.name];
    });
};
WndHandlerAttack.prototype.selectUnit = function (elm) {
    elm = $(elm);
    var prev = ~~elm.next().val();
    var count = ITowns.getTown(Game.townId).units()[elm[0].id];
    var newVal = prev === count ? 0 : count;
    elm.next().val(newVal);
    if (BuildingPlace.simulator_units && BuildingPlace.simulator_units.att) {
        BuildingPlace.simulator_units.att[elm[0].id] = newVal;
    }
};
WndHandlerAttack.prototype.sendUnits = function (sending_type, popup_type, target_id) {
    if (this.data.same_alliance) {
        var title = 'Attack on an alliance member';
        var text = 'Are you sure that you want to attack one of your alliance partners?';
        var that = this;
        return Layout.showConfirmDialog(title, text, function () {
            that.data.same_alliance = false;
            that.sendUnits(sending_type, popup_type, target_id);
        });
    }
    var params = {};
    this.getUnitInputs().each(function () {
        var name = this.name,
            value = ~~this.value;
        if (name && value) {
            params[name] = value;
        }
    });
    params.id = target_id;
    params.type = this.wnd.getJQElement().find('.attack_type_input').val() || sending_type;
    if (this.wnd.getJQElement().find('.attack_strategy_input').length) {
        params.attacking_strategy = this.wnd.getJQElement().find('.attack_strategy_input').val();
    }
    that = this;
    this.wnd.ajaxRequestPost(popup_type, 'send_units', params, function (window, data) {
        if (sending_type == 'support') {
            var support_count = $('#town_support_count');
            support_count.text((~~ (support_count.text())) + 1);
        } else {
            var attack_count = $('#town_attack_count');
            attack_count.text((~~ (attack_count.text())) + 1);
        }
        that.resetUnitInputs();
    }.bind(this));
};
WndHandlerAttack.prototype.render = function () {
    delete this.unitInputs;
    var html = tmpl2(GameData.TownAttackTemplate, this.data);
    var that = this;
    this.root = this.wnd.getJQElement();
    this.wnd.setContent(html);
    this.root.find('a.index_unit').bind('click', function () {
        that.selectUnit(this);
    });
    this.naval();
    this.bindDurationCounter();
    this.bindCapacityCounter();
    this.bindBootyCounter();
    this.root.bind('keydown.attack', function (ev) {
        var up = ev.keyCode === 38,
            down = ev.keyCode === 40,
            ele = ev.target;
        if ((up || down) && ele.nodeName === 'INPUT') {
            ele.value = Math.max(0, ~~ (ele.value) + (up ? 1 : -1));
        }
    });
    if (this.data.attack_types) {
        this.attType = this.root.find('div.attack_type');
        this.attType.bind('click', function (e) {
            that.setAttackType(e.target);
        });
    }
};
WndHandlerAttack.prototype.onRcvData = function (data) {
    this.data = data.json;
    this.data.preselect_units = this.data.preselect_units || false;
    this.same_island = data.json.same_island;
    var units = {};
    var i;
    for (i in data.json.units) {
        units[i] = data.json.units[i].count;
    }
    ITowns.getTown(Game.townId).units(units);
    if (data.units) {
        GameData.add({
            'units': data.units
        });
    }
    if (data.tmpl) {
        GameData.add({
            'TownAttackTemplate': data.tmpl
        });
    }
    this.render();
};
WndHandlerAttack.prototype.naval = function () {
    addIsNavalToGameDataUnits();
};
WndHandlerAttack.prototype.bindDurationCounter = function () {
    var that = this;
    var elm = {};
    elm.root = that.wnd.getJQElement();
    elm.container = elm.root.find('div.duration_container');
    elm.error = elm.root.find('div.duration_error_container');
    elm.error_text = elm.root.find('div.duration_error_text');
    elm.duration = elm.root.find('span.way_duration');
    elm.arrival = elm.root.find('span.arrival_time');
    elm.night = elm.root.find('span.nightbonus');

    function recalcDuration() {
        var naval = false,
            ground = false,
            transport = false;

        function findSlowestUnit() {
            var slowest_ground = null,
                slowest_naval = null,
                u = GameData.units;
            that.getUnitInputs().each(function () {
                var i = this.name,
                    count = ~~this.value,
                    is_naval;
                is_naval = u[i].is_naval;
                if (count) {
                    if (is_naval) {
                        slowest_naval = u[i].speed <= u[slowest_naval ? slowest_naval : i].speed ? i : slowest_naval;
                        naval = true;
                        transport = (i.indexOf('transporter') != -1) ? true : transport;
                    } else {
                        slowest_ground = u[i].speed <= u[slowest_ground ? slowest_ground : i].speed ? i : slowest_ground;
                        naval = false;
                        ground = true;
                    }
                }
            });
            return slowest_naval || slowest_ground;
        }

        function onlyFlyingUnits() {
            var flying_units = null;
            that.getUnitInputs().each(function () {
                var count = ~~this.value;
                var unit = GameData.units[this.name];
                if (count > 0 && !unit.is_naval) {
                    if (unit.flying === true && flying_units === null) {
                        flying_units = true;
                    } else {
                        flying_units = false;
                        return;
                    }
                }
            });
            return flying_units;
        }
        var slowest_unit = findSlowestUnit();
        if (slowest_unit !== null) {
            var arrival = Timestamp.server() + that.data.units[slowest_unit].duration;
            var arrival_in_timezone = new Date((arrival + Timestamp.serverGMTOffset()) * 1E3);
            var arrival_in_server_timezone = new Date((arrival + Timestamp.localeGMTOffset()) * 1E3);
            var prefix = that.farm_town || that.support ? '' : '~';
            elm.container.show();
            elm.error.hide();
            elm.duration.text(prefix + readableSeconds(that.data.units[slowest_unit].duration));
            elm.arrival.text(that.data.units[slowest_unit].duration).updateTime();
            elm.arrival.text('~' + readableUnixTimestamp(arrival, 'player_timezone'));
            if (!that.farm_town) {
                var is_night;
                var starts_at = that.data.night_starts_at_hour;
                var night_duration = that.data.night_duration;
                if (starts_at < 0) {
                    starts_at = 24 + starts_at;
                }
                if (night_duration === 0) {
                    is_night = false;
                } else if (starts_at + night_duration > 24) {
                    is_night = arrival_in_server_timezone.getUTCHours() <= (starts_at + night_duration - 1) % 24 || arrival_in_server_timezone.getUTCHours() >= starts_at;
                } else {
                    is_night = arrival_in_server_timezone.getUTCHours() <= (starts_at + night_duration - 1) && arrival_in_server_timezone.getUTCHours() >= starts_at;
                }
                if (is_night) {
                    elm.night.show();
                } else {
                    elm.night.hide();
                }
                var f = onlyFlyingUnits();
                var a = ground && !naval && !f && !that.same_island,
                    b = ground && naval && !f && !transport;
                if (a || b) {
                    elm.error_text.text((!transport && !naval) ? 'You have to send along ships across the ocean.' : 'You have to send transport boats along.');
                    elm.error.show();
                    elm.duration.hide();
                    elm.arrival.hide();
                    elm.container.hide();
                } else {
                    elm.error.hide();
                    elm.duration.show();
                    elm.arrival.show();
                    elm.container.show();
                }
            }
        } else {
            elm.container.hide();
        }
    }
    recalcDuration();
    elm.root.find('a.select_all_units').bind('click', recalcDuration);
    elm.root.find('a.index_unit').bind('click', recalcDuration);
    this.getUnitInputs().bind('keyup change', recalcDuration);
};

function wndhandler_foo(wndhandle) {
    var wnd = wndhandle;
    var that = this;
    this.getDefaultWindowOptions = function () {
        var ret = {
            position: ['center', 'center'],
            height: 300,
            width: 460,
            resizable: false,
            title: 'Foo! Window'
        };
        return ret;
    }
    this.onInit = function (title, UIopts) {
        var data = {};
        wnd.requestContentGet('wonders', 'index', null);
        return true;
    };
    this.onFinal = function () {};
    this.onClose = function () {
        return true;
    };
    this.onFocus = function () {};
    this.onSetContent = function (html) {
        return html;
    };
    this.onRcvData = function (data) {
        wnd.setContent(data['html']);
    };
    this.onMessage = function () {
        return null;
    };
    this.blubb = function () {
        console.log('yap' + arguments[0]);
    };
}

function WndHandlerBuild(wndhandle) {
    this.wnd = wndhandle;
    var that = this;
    this.clickHandlerMain = function (e) {
        var target = $(e.target).closest('a'),
            href = target.attr('href') || '',
            order_id, wnd, eta, eta_prev, i, orders, text;
        if (!href) {
            return;
        }
        e.preventDefault();
        href = href.split('#').reverse()[0].split(/\./);
        switch (href[0]) {
        case 'cancel':
            BuildOrders.confirm_cancel_building(that.wnd);
            break;
        case 'finishforgold':
            order_id = parseInt(href[1], 10);
            if (BuildOrders.availableGold >= BuildOrders.finishGoldOrderCost) {
                BuildOrders.confirm_finish_for_gold(order_id, null, target);
            } else {
                orders = BuildOrders.orders;
                for (i in orders) {
                    if (orders[i].id === order_id) {
                        eta = orders[i].to_be_completed_at;
                        eta_prev = i > 0 ? orders[i - 1].to_be_completed_at : 0;
                    }
                }
                BuildOrders.showNotEnoughGoldWindow(eta, eta_prev);
            }
            break;
        default:
            break;
        }
    };
}
WndHandlerBuild.inherits(WndHandlerDefault);

WndHandlerTowns = function () {};
WndHandlerTowns.inherits(WndHandlerAttack);
WndHandlerTowns.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        height: 400,
        width: 500,
        resizable: false,
        title: 'Untitled Window'
    };
    return ret;
};
WndHandlerTowns.prototype.updateTradeCapValue = function () {
    if ($('#avlbl_cap').length) {
        var values = [parseInt($('#trade_type_wood').val() || 0, 10), parseInt($('#trade_type_stone').val() || 0, 10), parseInt($('#trade_type_iron').val() || 0, 10)],
            sum = parseInt($('#avlbl_cap').val(), 10),
            i = values.length;
        while (i--) {
            sum -= values[i];
        }
        $('#left_cap').text(sum).css('color', sum < 0 ? '#f00' : '#000');
        slider_trade_type_wood.setMax(values[0] + sum);
        slider_trade_type_stone.setMax(values[1] + sum);
        slider_trade_type_iron.setMax(values[2] + sum);
    }
};
WndHandlerTowns.prototype.castPower = function (power_id, town_id, castedFromTownView, castedFromGodsOverview) {
    $('#towninfo_description div').hide();
    $('#casting_power').show();
    Layout.ajaxPost('town_info', 'cast', {
        'power': power_id,
        'id': town_id,
        'castedFromTownView': castedFromTownView
    }, false, function (data) {
        $('#towninfo_description div').hide();
        $('#power_casted').show().find('a').click(function () {
            Layout.viewReport(data.report_id);
        });
        if (castedFromGodsOverview == true && data.finished_at != null) {
            GodsOverview.updateTownsCastedPowers(town_id, data.casted_power_id, data.finished_at);
        } else {
            if (typeof data.finished_at == 'number') {
                GPEvents.global.trigger(GPEvents.global.overviews.power_casted, {
                    'power': power_id,
                    'id': town_id,
                    'data': data
                });
            }
        }
        if (data.bar && data.bar.favors) {
            GPEvents.global.trigger(GPEvents.global.overviews.favor_changed, data.bar.favors);
        }
    }.bind(this));
};

function WndHandlerTown(wndhandle) {
    this.currentTownID = -1;
    this.wnd = wndhandle;
    this.sameIsland = null;
    this.action = null;
    this.target_id = null;
}
WndHandlerTown.inherits(WndHandlerTowns);
WndHandlerTown.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        maxHeight: 380,
        maxWidth: 500,
        height: 380,
        width: 500,
        resizable: false,
        minimizable: true,
        title: ''
    };
    return ret;
};
WndHandlerTown.prototype.onInit = function (title, UIopts, arg3) {
    this.wnd.requestContentGet('town_info', UIopts.action, arg3);
    return true;
};
WndHandlerTown.prototype.onClose = function () {
    if (this.espionage_class) {
        this.espionage_class.cleanUp();
    }
    this.unregisterEventListeners();
    return true;
};
WndHandlerTown.prototype.onRcvData = function (data, controller, action) {
    var html;
    this.action = action;
    if (data && data.json && data.json.target_id) {
        this.target_id = data.json.target_id;
    }
    this.unregisterEventListeners();
    switch (action) {
    case 'attack':
        this.parent.parent.onRcvData.apply(this, arguments);
        this.registerEventListeners();
        break;
    case 'espionage':
        if (data.html) {
            html = data.html;
        }
        this.wnd.setContent(html);
        this.target_id = data.target_id;
        this.espionage_class = new Espionage(this.target_id, this.wnd, data.stored_iron);
        this.registerEventListeners();
        break;
    case 'support':
        this.parent.parent.onRcvData.apply(this, arguments);
        this.registerEventListeners();
        break;
    case 'god':
        var tmpl = data.tmpl.split('|||'),
            powers = tmpl.shift(),
            descr = tmpl.pop();
        GameData.add({
            'PowerDescriptionTemplate': descr
        });
        html = tmpl2(powers, data.json);
        this.wnd.setContent(html);
        break;
    case 'trading':
        if (data.html) {
            html = data.html;
        }
        this.wnd.setContent(html);
        var avlbl_cap = $("#avlbl_cap").val();
        slider_trade_type_wood.setMax(avlbl_cap);
        slider_trade_type_stone.setMax(avlbl_cap);
        slider_trade_type_iron.setMax(avlbl_cap);
        break;
    default:
        if (data.html) {
            html = data.html;
        }
        this.wnd.setContent(html);
        break;
    }
};
WndHandlerTown.prototype.getPage = function (arg1, arg2) {
    this.wnd.requestContentGet('town_info', arg1, {
        id: arg2
    });
    return null;
};
WndHandlerTown.prototype.showPowerDescription = function (power) {
    this.descr = this.wnd.getJQElement().find('#towninfo_description');
    this.descr.empty().hide().html(tmpl2(GameData.PowerDescriptionTemplate, GameData.powers[power])).fadeIn('slow');
};
WndHandlerTown.prototype.trade = function (town_id) {
    Layout.ajaxPost('town_info', 'trade', {
        'id': town_id,
        'wood': $('#trade_type_wood').val(),
        'stone': $('#trade_type_stone').val(),
        'iron': $('#trade_type_iron').val()
    }, false, function (data) {
        slider_trade_type_wood.setValue(0);
        slider_trade_type_stone.setValue(0);
        slider_trade_type_iron.setValue(0);
    }.bind(this), {}, 'town_info_trade');
};
WndHandlerTown.prototype.inviteIntoAlliance = function (player_name) {
    this.wnd.ajaxRequestPost('alliance', 'invite', {
        player_name: player_name
    }, function (_wnd, return_data) {});
};
WndHandlerTown.prototype.setAttackType = function (elm) {
    var type;
    var class_name_parts;
    var class_names = (elm.className).split(' ');
    var i = class_names.length;
    while (i--) {
        var class_name = class_names[i];
        if (class_name != 'attack_type_active' && class_name != 'attack_type') {
            class_name_parts = class_name.split('_');
            if (class_name_parts.length == 3) {
                type = class_name_parts[2];
                break;
            }
        }
    }
    $(elm.parentNode).find('.attack_type').removeClass('attack_type_active');
    $(elm).addClass('attack_type_active');
    $(elm.parentNode).find('.attack_strategy_input').val(type);
};
WndHandlerTown.prototype.registerEventListeners = function () {
    var that = this;
    GPEvents.global.bind(GPEvents.global.town.units_changed + '.WndHandlerTown' + this.wnd.getID(), function (e) {
        that.handleEvents(e, that);
    });
    GPEvents.global.bind(GPEvents.global.town.town_switch + '.WndHandlerTown' + this.wnd.getID(), function (e) {
        that.handleEvents(e, that);
    });
    GPEvents.global.bind(GPEvents.global.town.hide_has_changed + '.WndHandlerTown' + this.wnd.getID(), function (e, d) {
        that.handleEvents(e, that, d);
    });
};
WndHandlerTown.prototype.unregisterEventListeners = function () {
    GPEvents.global.unbind('.WndHandlerTown' + this.wnd.getID());
};
WndHandlerTown.prototype.handleEvents = function (event, that) {
    if (event.type == GPEvents.global.town.units_changed) {
        if (that.data) {
            var root = that.wnd.getJQElement();
            var units = ITowns.getTown(Game.townId).units();
            var unit_name;
            for (unit_name in that.data.units) {
                if (!that.data.units.hasOwnProperty(unit_name)) {
                    continue;
                }
                that.data.units[unit_name].count = units[unit_name];
                root.find('a#' + unit_name).children('span').html(that.data.units[unit_name].count);
            }
        }
    }
    if (event.type == GPEvents.global.town.town_switch || event.type == GPEvents.global.town.hide_has_changed) {
        this.wnd.requestContentGet('town_info', this.action, {
            'id': this.target_id
        });
    }
};

function WndHandlerIsland(wndhandle) {
    this.wnd = wndhandle;
    this.island = null;
}
WndHandlerIsland.inherits(WndHandlerDefault);
WndHandlerIsland.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        height: 580,
        width: 700,
        resizable: false,
        title: 'Island info'
    };
    return ret;
};
WndHandlerIsland.prototype.onInit = function (title, UIopts, isleObj) {
    var island = this.island = isleObj,
        params = {
            island_id: island.id
        };
    if (!GameData.IslandInfoTemplate) {
        params.fetch_tmpl = 1;
    }
    this.wnd.requestContentGet('island_info', 'index', params);
    return true;
};
WndHandlerIsland.prototype.onRcvData = function (data) {
    data.json.island = this.island;
    if (data.tmpl) {
        GameData.add({
            'IslandInfoTemplate': data.tmpl
        });
    }
    var html = tmpl2(GameData.IslandInfoTemplate, data.json),
        commercial;
    this.wnd.setContent2(html);
    $('#island_bbcode_link').bind('click', function () {
        $('#island_bbcode_id').toggle().focus();
    });
    $('#link_to_alliance_profile').bind('click', function (e) {
        var target = e.target;
        Layout.allianceProfile.open(target.getAttribute("title"), target.getAttribute("name"));
    });
    $("#farm_town_overview_btn").click(function () {
        farmTownOverview.openFarmTownOverviewWindow();
    }).mousePopup(new MousePopup("Farming villages overview"));
    $("#island_towns_sort").change(function (e) {
        var value = $(this).val();
        if (value == "name") {
            $('#island_info_towns_left_sorted_by_score').hide();
            $('#island_info_towns_left_sorted_by_name').show();
        } else if (value == "score") {
            $('#island_info_towns_left_sorted_by_name').hide();
            $('#island_info_towns_left_sorted_by_score').show();
        }
    });
    this.wnd.getJQElement().find('div.captain_commercial').show().find('a.button').mousePopup(new MousePopup(PopupFactory.texts.captain_hint));
};

function WndHandlerTownIndex(wndhandle) {
    this.currentTownID = -1;
    this.currentTownName = '';
    this.wnd = wndhandle;
    this.onclose_hook = null;
    this.cachedData = null;
    this.buildFromTownIndexEnabled = null;
}
WndHandlerTownIndex.inherits(WndHandlerBuild);
WndHandlerTownIndex.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        'height': 585,
        'width': 752,
        'resizable': false,
        'title': 'TownIndex - Unknown',
        'fullwindow': true,
        'minimizable': true
    };
    return ret;
};
WndHandlerTownIndex.prototype.onInit = function (title, UIopts, arg_2, arg_3) {
    this.wnd.sendMessage('setTown', arg_2, arg_3, true);
    return true;
};
WndHandlerTownIndex.prototype.onFocus = function () {
    var quest_wnd = Layout.wnd.getOpenFirst(Layout.wnd.TYPE_QUEST);
    if (quest_wnd != undefined) {
        quest_wnd.toTop();
    }
};
WndHandlerTownIndex.prototype.onClose = function () {
    if (this.onclose_hook) {
        if (!this.onclose_hook()) {
            HumanMessage.error('This window can\'t be closed. Finish the tutorial first.');
            return false;
        }
    }
    this.unregisterEventListeners();
    Layout.townIndex.close(true);
    return true;
};
WndHandlerTownIndex.prototype.onRcvData = function (data) {
    this.cachedData = data;
    $('#gpwnd_' + this.wnd.getID().toString()).css({
        'overflow-y': 'hidden'
    });
    if (data.buildings) {
        GameData.add({
            'buildings': data.buildings
        });
    }
    if (data.tmpl) {
        GameData.add({
            'TownIndexTemplate': data.tmpl
        });
    }
    if (data.building_orders_tmpl) {
        GameData.add({
            'BuildingOrdersTemplate': data.building_orders_tmpl,
            'BuildingOrdersTemplateNew': data.building_orders_tmpl_new,
            'abctest_time_reduction': data.abctest_time_reduction
        });
    }
    this.refresh(data);
    var that = this;
    $('#index_toggle_hammer').unbind('click').bind('click', function () {
        that.toggleBuildOn();
    });
};
WndHandlerTownIndex.prototype.refresh = function (data) {
    var html, elem, i;
    if (data.build_from_town_index_enabled !== undefined) {
        this.buildFromTownIndexEnabled = data.build_from_town_index_enabled;
    }
    $('#town_background area').unbind().remove();
    html = tmpl2(GameData.TownIndexTemplate, data);
    this.wnd.setContent2(html);
    BuildingAnimation.init();
    BuildOrders.init(data.json_orders, data.available_gold, data.finish_building_order_cost, data.remaining_reductions_until_free, data.is_finish_order_campaign_active);
    BuildOrders.setLength(ITowns.getTown(Game.townId).buildingOrdersETC());
    BuildOrders.updateBuildingOrders();
    elem = $('#town_background');
    for (i in data.building_orders_completed_ts) {
        elem.find('div.index_building_timer.index_' + i + '_building_timer').countdown();
    }
    this.bindBuildingOpenHandlers(data);
    var that = this;
    $('#index_toggle_hammer').unbind('click').bind('click', function () {
        that.toggleBuildOn();
    });
    this.wnd.getJQElement().find('div.advisor_hint').find('a.button').mousePopup(new MousePopup(PopupFactory.texts.curator_hint));
};
WndHandlerTownIndex.prototype.toggleBuildOn = function () {
    if (Tutorial.getActiveQuest() == Tutorial.quests.basic_tutorial) {
        return;
    }
    $("#index_map_image").css('z-index', 4);
    $("#index_hammers").css('z-index', 5);
    var toggle_hammer = $('#index_toggle_hammer').addClass('index_toggle_hammer_pressed');
    var that = this;
    this.wnd.ajaxRequestGet('town_info', 'get_build_info', [], function (window, data) {
        that.addHammers(data);
        toggle_hammer.unbind('click').click(function () {
            that.toggleBuildOff();
        });
    });
};
WndHandlerTownIndex.prototype.addHammers = function (data) {
    var that = this;
    jQuery.each(data.town_buildings, function (buildingId, building) {
        if ((building.get_dependencies.length === 0) && !building.max_level) {
            var elHammer = $('<a href="#" class="index_hammer index_' + buildingId + '_building_hammer">');
            if (!building.enough_population) {
                elHammer.addClass('index_hammer_pop');
            } else if (!building.enough_resources) {
                elHammer.addClass('index_hammer_res');
            } else if (data.full_queue) {
                elHammer.addClass('index_hammer_queue');
            } else {
                elHammer.addClass('index_hammer_ok');
                elHammer.bind('click', function () {
                    that.build(buildingId);
                });
            }
            elHammer.mousePopup(new MousePopup(getUpgradeBuildingData(building, false)[0]));
            $('#index_hammers').append(elHammer);
            if (building.level === 0) {
                var elBuilding = $('<img id="index_' + buildingId + '" src="' + Game.img() + '/game/visual2/' + buildingId + '0.png" alt=""/>');
                elBuilding.fadeTo("fast", 0.5);
                $('#index_translucent').append(elBuilding);
            }
        }
    });
};
WndHandlerTownIndex.prototype.build = function (buildingId) {
    BuildingMain.buildBuilding(buildingId);
};
WndHandlerTownIndex.prototype.toggleBuildOff = function () {
    var toggle_hammer = $('#index_toggle_hammer');
    var index_hammers = $('#index_hammers');
    $("#index_map_image").css('z-index', 5);
    index_hammers.css('z-index', 4);
    toggle_hammer.removeClass('index_toggle_hammer_pressed');
    index_hammers.empty();
    $('#index_translucent').empty();
    this.bindBuildingOpenHandlers(this.cachedData);
    var that = this;
    toggle_hammer.unbind('click').bind('click', function () {
        that.toggleBuildOn();
    });
};
WndHandlerTownIndex.prototype.bindBuildingOpenHandlers = function (data) {
    var map = $('#map_town'),
        areas = map.find('area');
    if (jQuery.browser.safari) {
        if (!map.length) {
            map = $('#' + this.old_map_id);
        }
        map[0].id += Date.now().toString(16);
        map[0].name = map[0].id;
        this.old_map_id = map[0].id;
        document.getElementById('index_map_image').useMap = map[0].id;
    }
    areas.each(function () {
        var i = this.id.replace('building_main_area_', '');
        if (i.localeCompare('phoenician_salesman') !== 0) {
            $(this).unbind().mousePopup(new MousePopup(s('%1 (level %2)', GameData.buildings[i].name, data.buildings_array[i]))).click(function () {
                Layout.buildingWindow.open(i);
            });
        } else {
            $(this).unbind().mousePopup(new MousePopup('Phoenician merchant')).click(function () {
                Layout.phoenicianSalesman.open();
            });
        }
    });
};
WndHandlerTownIndex.prototype.onMessage = function (name, arg_1, arg_2, arg_3) {
    switch (name) {
    case 'setTown':
        this.unregisterEventListeners();
        var required_data = 0;
        if (this.currentTownID === ~~arg_1) {
            return null;
        }
        if (!GameData.buildings) {
            required_data |= 1;
        }
        if (!GameData.TownIndexTemplate) {
            required_data |= 2;
        }
        if (!GameData.BuildingOrdersTemplate) {
            required_data |= 4;
        }
        this.currentTownID = ~~arg_1;
        this.currentTownName = arg_2;
        this.wnd.requestContentGet('town_info', 'go_to_town', {
            'town_id': arg_1,
            'required_data': required_data
        });
        if (!arg_3) {
            this.wnd.setTitle('City view' + ' - ' + arg_2);
        }
        this.registerEventListeners();
        break;
    case 'getData':
        break;
    case 'setOnCloseHook':
        this.onclose_hook = arg_1;
        break;
    case 'unsetOnCloseHook':
        this.onclose_hook = null;
        break;
    case 'togglePhoenicianSalesmanAndReRender':
        this.cachedData.has_phoenician_salesman = !this.cachedData.has_phoenician_salesman;
        this.onRcvData(this.cachedData);
        break;
    }
    return null;
};
WndHandlerTownIndex.prototype.registerEventListeners = function () {
    var that = this,
        g = GPEvents.global;
    g.bind(g.town.units_changed + '.WndHandlerTownIndex', function (e) {
        that.handleEvents(e, that);
    });
    g.bind(g.town.building_order_changed + '.WndHandlerTownIndex', BuildOrders.handleEvents);
    this.wnd.getJQElement().click(that.clickHandlerMain);
};
WndHandlerTownIndex.prototype.unregisterEventListeners = function () {
    GPEvents.global.unbind('.WndHandlerTownIndex');
};
WndHandlerTownIndex.prototype.handleEvents = function (event, that) {
    if (event.type == GPEvents.global.town.units_changed) {
        if (that.cachedData) {
            var units = ITowns.getTown(Game.townId).units();
            if (units.colonize_ship > 0 && !that.cachedData.has_colonize_ship) {
                that.cachedData.has_colonize_ship = true;
                that.onRcvData(that.cachedData);
            }
            if (units.colonize_ship === 0 && that.cachedData.has_colonize_ship) {
                that.cachedData.has_colonize_ship = false;
                that.onRcvData(that.cachedData);
            }
        }
    }
};

function WndHandlerFarmTown(wndhandle) {
    this.wnd = wndhandle;
    this.farm_town = true;
}
WndHandlerFarmTown.inherits(WndHandlerAttack);
WndHandlerFarmTown.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        height: 200,
        width: 560,
        resizable: false,
        title: '',
        autoresize: true
    };
    return ret;
};
WndHandlerFarmTown.prototype.onInit = function (title, UIopts, id) {
    this.wnd.requestContentGet('farm_town_info', UIopts.action, {
        id: id
    });
    return true;
};
WndHandlerFarmTown.prototype.onClose = function () {
    this.unregisterEventListeners();
    return true;
};
WndHandlerFarmTown.prototype.afterSetContent = function () {
    if (Tutorial.getActiveQuest() == Tutorial.quests.farm_town_tutorial) {
        $('a.farm_tut_button').hide();
    }
    $('.farm_attack_troops_troops').find('input[type="text"]').each(function () {
        $(this).blur(function () {
            parseToValidNumericValue(this);
            BuildingPlace.simulator_units.att[$(this).attr('name')] = $(this).val();
        });
    });
};
WndHandlerFarmTown.prototype.render = function () {
    var that = this,
        html;
    delete this.unitInputs;
    html = tmpl2(GameData.FarmAttackTemplate, this.data);
    this.wnd.setContent(html);
    this.afterSetContent();
    this.root.find('a.index_unit').bind('click', function () {
        that.selectUnit(this);
    });
    this.root.find('a.button[name=send_units]').click(function () {
        that.sendUnits('', that.data.type, that.data.target);
    });
    this.bindDurationCounter();
    this.root.find('div.farm_attack_bar_bg').mousePopup(new MousePopup('Required unit strength to conquer another farming village'));
    this.root.find('a.button.simulate_units').mousePopup(new MousePopup('Add units to the simulator'));
    this.wnd.setTitle('Attack on %s'.replace('%s', this.data.farm_town_name));
};
WndHandlerFarmTown.prototype.onRcvData = function (data, controller, action) {
    var units, i;
    this.unregisterEventListeners();
    this.root = this.wnd.getJQElement();
    switch (action) {
    case 'attack':
        this.data = data.json;
        units = {};
        for (i in data.json.units) {
            units[i] = data.json.units[i].count;
        }
        ITowns.getTown(Game.townId).units(units);
        if (data.units) {
            GameData.add({
                'units': data.units
            });
        }
        if (data.tmpl) {
            GameData.add({
                'FarmAttackTemplate': data.tmpl
            });
        }
        this.render();
        break;
    case 'trading':
        this.wnd.setContent(data.html);
        $('#demand').mousePopup(new MousePopup('You are exchanging this resource.'));
        $('#offer').mousePopup(new MousePopup('You are receiving this resource.'));
        $('#arrival_time').mousePopup(new MousePopup('Arrival'));
        $('#way_duration').mousePopup(new MousePopup('Travel time'));
        var trade_ratio = this.root.find('span.trade_ratio').html(),
            min_trading_sum = parseInt(this.root.find('a.trade_slider_min').html(), 10),
            max_trading_sum = parseInt(this.root.find('a.trade_slider_max').html(), 10);
        this.bindTradeSlider(trade_ratio, min_trading_sum, max_trading_sum);
        this.wnd.setTitle('Trade with %s'.replace('%s', data.farm_town_name));
        break;
    case 'claim_info':
    case 'pillage_info':
    case 'units_info':
        this.wnd.setContent(data.html);
        var timer = this.root.find('span.farm_next_claim_time');
        timer.countdown(data.json.lootable_at, {});
        var that_wnd = this;
        timer.bind('finish', function () {
            that_wnd.root.find('span.farm_next_claim_time').html('ready');
            that_wnd.root.find('a.farm_claim_button').each(function () {
                $(this).removeClass('farm_claim_button_inactive');
            });
        });
        var w_title = '';
        switch (action) {
        case 'claim_info':
            w_title = 'Demand from %s'.replace('%s', data.json.farm_town_name);
            this.wnd.getJQElement().find('div.captain_commercial').show().find('a.button').mousePopup(new MousePopup(PopupFactory.texts.captain_hint));
            break;
        case 'pillage_info':
            w_title = 'Loot %s'.replace('%s', data.json.farm_town_name);
            break;
        case 'units_info':
            w_title = 'Demand units from %s'.replace('%s', data.json.farm_town_name);
            break;
        }
        this.wnd.setTitle(w_title);
        break;
    default:
        this.wnd.setContent(data.html);
        var farm_town_name = '';
        farm_town_name = (data.farm_town_name) ? data.farm_town_name : farm_town_name;
        farm_town_name = (data.json) ? data.json.farm_town_name : farm_town_name;
        this.wnd.setTitle('Expansion of %s'.replace('%s', farm_town_name));
    }
    this.registerEventListeners(action);
    this.afterSetContent();
};
WndHandlerFarmTown.prototype.bindTradeSlider = function (ratio, min, max) {
    var root = this.root.find('div.trade_slider_box');
    var element_slider = root.find('div.trade_slider_slider'),
        trade_slider = new Slider({
            elementMin: root.find('a.trade_slider_min'),
            elementMax: root.find('a.trade_slider_max'),
            elementDown: root.find('a.trade_slider_down'),
            elementUp: root.find('a.trade_slider_up'),
            elementInput: root.find('input.trade_slider_input'),
            elementSlider: element_slider,
            elementDownFast: root.find('a.trade_slider_ffwd_down'),
            elementUpFast: root.find('a.trade_slider_ffwd_up')
        });
    element_slider.bind('change', function () {
        var input = trade_slider.getValue(),
            output = Math.round(input * ratio);
        $('input.trade_slider_output').val(output);
        $('#trade_out').text(output);
        $('.trade_in').text(input);
    });
    trade_slider.setMin(min);
    trade_slider.setMax(max);
    trade_slider.setValue(min);
};
WndHandlerFarmTown.prototype.tradeWithFarmTown = function (target_id) {
    var trade_input = this.wnd.getJQElement().find('input.trade_slider_input').val();
    this.wnd.ajaxRequestPost('farm_town_info', 'trade', {
        'id': target_id,
        'trade_input': trade_input
    }, function (wnd) {
        wnd.close();
    }, {}, 'town_info_trade_with_farm_town');
};
WndHandlerFarmTown.prototype.sendResources = function (target_id) {
    var that = this,
        wood = $('#trade_type_wood_' + target_id),
        stone = $('#trade_type_stone_' + target_id),
        iron = $('#trade_type_iron_' + target_id),
        ajax_data = {
            target_id: target_id,
            wood: wood.val(),
            stone: stone.val(),
            iron: iron.val()
        };
    this.wnd.ajaxRequestPost('farm_town_info', 'send_resources', ajax_data, function (_wnd, data) {
        that.wnd.reloadContent();
        WMap.updateStatusInChunkTowns(target_id, -1, -1, -1, '', 1, data.expansion_stage);
        WMap.refresh('towns');
    });
};
WndHandlerFarmTown.prototype.claimLoad = function (target_id, claim_type, time) {
    this.wnd.ajaxRequestPost('farm_town_info', 'claim_load', {
        target_id: target_id,
        claim_type: claim_type,
        time: time
    }, function (window, data) {
        window.close();
        if (data.relation_status == 2) {
            WMap.updateStatusInChunkTowns(target_id, data.satisfaction, Timestamp.now() + time, Timestamp.now(), data.lootable_human, 2);
            WMap.pollForMapChunksUpdate();
            WMap.changeFarmTownFlag(target_id, 2);
        } else {
            WMap.updateStatusInChunkTowns(target_id, data.satisfaction, Timestamp.now() + time, Timestamp.now(), data.lootable_human);
        }
        WMap.hideLoadIcon(target_id);
    });
};
WndHandlerFarmTown.prototype.claimUnits = function (target_id, _unit_type) {
    this.wnd.ajaxRequestPost('farm_town_info', 'claim_units', {
        target_id: target_id,
        unit_type: _unit_type
    }, function (window, data) {
        window.close();
        if (data.relation_status == 2) {
            WMap.updateStatusInChunkTowns(target_id, data.satisfaction, Timestamp.now() + data.time, Timestamp.now(), data.lootable_human, 2);
            WMap.pollForMapChunksUpdate();
            WMap.changeFarmTownFlag(target_id, 2);
        } else {
            WMap.updateStatusInChunkTowns(target_id, data.satisfaction, Timestamp.now() + data.time, Timestamp.now(), data.lootable_human);
        }
        WMap.hideLoadIcon(target_id);
    });
};
WndHandlerFarmTown.prototype.registerEventListeners = function (open_tab) {
    var that = this;
    if (open_tab === 'attack') {
        GPEvents.global.bind(GPEvents.global.town.units_changed + '.WndHandlerFarmTown' + this.wnd.getID(), function (e) {
            that.handleEvents(e, that);
        });
    }
    GPEvents.global.bind(GPEvents.global.town.town_switch + '.WndHandlerFarmTown' + this.wnd.getID(), function (e) {
        that.handleEvents(e, that);
    });
};
WndHandlerFarmTown.prototype.unregisterEventListeners = function () {
    GPEvents.global.unbind('.WndHandlerFarmTown' + this.wnd.getID());
};
WndHandlerFarmTown.prototype.handleEvents = function (event, that) {
    var root, units, unit_name;
    if (event.type == GPEvents.global.town.units_changed) {
        if (!that.data) {
            return;
        }
        root = that.wnd.getJQElement();
        units = ITowns.getTown(Game.townId).units();
        for (unit_name in that.data.units) {
            if (!that.data.units.hasOwnProperty(unit_name)) {
                continue;
            }
            that.data.units[unit_name].count = units[unit_name];
            root.find('a#' + unit_name).children('span').html(that.data.units[unit_name].count);
        }
    } else if (event.type == GPEvents.global.town.town_switch) {
        that.wnd.close();
    }
};

function WndHandlerTownOverviews(wndhandle) {
    this.wnd = wndhandle;
    this.controller = null;
    this.action = null;
}
WndHandlerTownOverviews.inherits(WndHandlerTowns);
WndHandlerTownOverviews.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        maxHeight: 580,
        maxWidth: 780,
        height: 580,
        width: 780,
        resizable: false,
        title: 'TownOverviews - XX'
    };
    return ret;
};
WndHandlerTownOverviews.prototype.onInit = function (title, UIopts, town_id) {
    UIopts.controller = UIopts.controller || 'town_overviews';
    this.controller = UIopts.controller;
    this.action = UIopts.overview_type;
    this.wnd.requestContentGet(UIopts.controller, UIopts.overview_type, {
        'town_id': town_id
    });
    return true;
};
WndHandlerTownOverviews.prototype.loadTab = function (controller, action) {
    this.controller = controller;
    this.action = action;
    var img = document.createElement('img');
    img.id = 'ajax_loader';
    img.src = "http://cdn.grepolis.com/images/game/ajax-loader.gif";
    img.alt = '';
    img.style.top = ((this.wnd.getJQElement().height() >> 1) - 58) + 'px';
    img.style.left = ((this.wnd.getJQElement().width() >> 1) - 29) + 'px';
    img.position = 'relative';
    this.wnd.appendContent(img);
    this.onReload();
    return true;
};
WndHandlerTownOverviews.prototype.onReload = function () {
    if (this.action && this.controller) {
        this.unregisterEventListeners();
        this.wnd.requestContentGet(this.controller, this.action, {});
    }
    return true;
};
WndHandlerTownOverviews.prototype.onClose = function () {
    $(document).stopTime('WndHandlerTownOverviews::initializeResourcesProductionCounter::timer');
    this.unregisterEventListeners();
    return true;
};
WndHandlerTownOverviews.prototype.onRcvData = function (data, controller, action) {
    var that = this,
        foo = false;
    switch (action) {
    case 'trade_overview':
        this.wnd.setContent2(data.tmpl2);
        this.tradeOverview = new Overviews.TradeOverview(data.towns, data.town_tmpl, data.movements, data.mov_tmpl);
        this.wnd.getJQElement().children('div.gpwindow_content').addClass('fullwindow');
        break;
    default:
        if (this.tradeOverview) {
            this.tradeOverview.destroy();
            delete this.tradeOverview;
        }
        this.wnd.getJQElement().children('div.gpwindow_content').removeClass('fullwindow');
        foo = true;
        break;
    }
    if (foo) {
        $(document).stopTime('WndHandlerTownOverviews::initializeResourcesProductionCounter::timer');
        this.wnd.setContent2(data.html);
        this.wnd.clearMenu();
        this.unregisterEventListeners();
        this.registerEventListeners();
    }
    if (controller == "town_overviews" && action == "gods_overview") {
        GodsOverview.init(data.templates, data.data);
    } else if (controller == "town_overviews" && action == "command_overview") {
        CommandsOverview.init(data.templates, data.data);
    }
};
WndHandlerTownOverviews.prototype.getPage = function () {
    return null;
};
WndHandlerTownOverviews.prototype.initializeResourcesCounter = function (towns) {
    var that = this;
    var step = 5000;
    var town, town_id;
    for (town_id in towns) {
        if (!towns.hasOwnProperty(town_id)) {
            continue;
        }
        town = towns[town_id];
        ITowns.setResources(town.resources, town.id);
        ITowns.setProduction(town.production, town.id);
        ITowns.setStorage(town.storage_volume, town.id);
    }
    $(document).everyTime(step, 'WndHandlerTownOverviews::initializeResourcesProductionCounter::timer', function () {
        that.updateResources();
    });
};
WndHandlerTownOverviews.prototype.updateResources = function () {
    var resources = ['wood', 'stone', 'iron'],
        towns = ITowns.getTowns(),
        town, town_id, res, i, elem;
    for (town_id in towns) {
        if (!towns.hasOwnProperty(town_id)) {
            continue;
        }
        ITowns.updateResourceCounter(town_id);
        town = towns[town_id];
        res = town.resources();
        i = resources.length;
        while (i--) {
            var id = resources[i];
            if (this.controller + '.' + this.action != 'town_overviews.towns_overview') {
                elem = $('#town_' + town_id + '_res .' + id + '.resource_count .count');
            } else {
                elem = $('#town_' + town_id + '_res .' + id);
            }
            if (!elem.length) {
                continue;
            }
            if (parseInt(res[id], 10) >= parseInt(res.storage, 10)) {
                elem.addClass('town_storage_full');
                elem.html(res.storage);
            } else {
                if (elem.hasClass('town_storage_full')) {
                    elem.removeClass('town_storage_full');
                }
                elem.html(res[id]);
            }
        }
        if (town_id == Game.townId) {
            if (res.population) {
                elem = $('#town_' + town_id + '_res .town_population .count');
                elem = (elem.length) ? elem : $('#town_' + town_id + '_res .town_population');
                elem = (elem.length) ? elem : $('#town_' + town_id + '_res .town_population_count');
                if (elem.length) {
                    elem.html(res.population);
                }
            }
            elem = $('#town_' + town_id + '_tinfo .storage');
            elem = (elem.length) ? elem : $('#town_' + town_id + '_res .storage');
            if (elem.length) {
                elem.html(res.storage);
            }
        }
    }
};
WndHandlerTownOverviews.prototype.handleUnitUpdate = function (event) {
    if (event.type == GPEvents.global.town.units_changed) {
        var units = ITowns.getTown(Game.townId).units(),
            unit_id;
        for (unit_id in units) {
            if (!units.hasOwnProperty(unit_id)) {
                continue;
            }
            if (this.controller + '.' + this.action == 'town_overviews.towns_overview') {
                $('#units_div_' + Game.townId + ' .' + unit_id).html(units[unit_id]);
            } else {
                $('#units_' + Game.townId + ' .unit_' + unit_id + ' .place_unit_black').html(units[unit_id]);
                $('#units_' + Game.townId + ' .unit_' + unit_id + ' .place_unit_white').html(units[unit_id]);
            }
        }
    }
};
WndHandlerTownOverviews.prototype.registerEventListeners = function () {
    var that = this;
    switch (this.controller + '.' + this.action) {
    case 'town_overviews.command_overview':
        GPEvents.global.bind(GPEvents.global.town.commands_updated + '.WndHandlerTownOverviews', function () {
            that.onReload();
        });
        break;
    case 'town_overviews.unit_overview':
        GPEvents.global.bind(GPEvents.global.town.units_changed + '.WndHandlerTownOverviews', function (event) {
            that.handleUnitUpdate(event);
        });
        break;
    case 'town_overviews.gods_overview':
        GPEvents.global.bind(GPEvents.global.overviews.power_casted + '.WndHandlerTownOverviews', GodsOverview.updateGodsOverview);
        GPEvents.global.bind(GPEvents.global.overviews.favor_changed + '.WndHandlerTownOverviews', GodsOverview.updateFavorBar);
        break;
    case 'town_overviews.towns_overview':
        GPEvents.global.bind(GPEvents.global.town.units_changed + '.WndHandlerTownOverviews', function (event) {
            that.handleUnitUpdate(event);
        });
        break;
    default:
        break;
    }
};
WndHandlerTownOverviews.prototype.unregisterEventListeners = function () {
    GPEvents.global.unbind('.WndHandlerTownOverviews');
    if (this.tradeOverview) {
        this.tradeOverview.destroy();
        delete this.tradeOverview;
    }
};

function WndHandlerWonders(wndhandle) {
    this.wnd = wndhandle;
    this.island_x = 0;
    this.island_y = 0;
    this.wonder = null;
}
WndHandlerWonders.inherits(WndHandlerDefault);
WndHandlerWonders.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        height: 520,
        width: 762,
        resizable: false,
        title: 'Construction site for a World Wonder'
    };
    return ret;
};
WndHandlerWonders.prototype.onInit = function (title, UIopts, island_x, island_y) {
    this.island_x = island_x;
    this.island_y = island_y;
    this.wnd.requestContentGet('wonders', 'index', {
        island_x: island_x,
        island_y: island_y
    });
    return true;
};
WndHandlerWonders.prototype.onRcvData = function (data) {
    this.wnd.setContent2(data.html);
    this.unregisterEventListeners();
    this.registerEventListeners();
};
WndHandlerWonders.prototype.onMessage = function () {
    return null;
};
WndHandlerWonders.prototype.buildWonder = function () {
    this.wnd.ajaxRequestPost('wonders', 'build_wonder', {
        island_x: this.island_x,
        island_y: this.island_y,
        wonder_type: this.wonder
    }, function (_wnd, return_data) {
        _wnd.setContent2(return_data.html);
        _wnd.setTitle(return_data.wonder_name);
    });
};
WndHandlerWonders.prototype.decreaseBuildTimeWithFavor = function (wonder_id) {
    this.wnd.requestContentPost('wonders', 'decrease_build_time_with_favor', {
        island_x: this.island_x,
        island_y: this.island_y
    });
};
WndHandlerWonders.prototype.chooseWonder = function (wonder_type) {
    var root = this.wnd.getJQElement();
    var descriptions = root.find('div.wonder_descriptions');
    var confirm_button = root.find('div.confirm_wonder_button');
    if (this.wonder != null) {
        descriptions.find('li.' + this.wonder).hide();
    }
    this.wonder = wonder_type;
    root.find('div.wonder_info_text_wrapper').hide();
    root.find('ul.wonder_building_options > li').removeClass('selected').filter('li.' + wonder_type).addClass('selected');
    descriptions.show().find('li.' + wonder_type).show();
    descriptions.append(confirm_button.show());
};
WndHandlerWonders.prototype.sendResources = function () {
    var root = this.wnd.getJQElement();
    var wood = root.find('#wonder_type_wood').val();
    var stone = root.find('#wonder_type_stone').val();
    var iron = root.find('#wonder_type_iron').val();
    if (wood == '' && stone == '' && iron == '') {
        HumanMessage.error('No resource has been selected');
    } else {
        this.wnd.requestContentPost('wonders', 'send_resources', {
            wood: wood,
            stone: stone,
            iron: iron,
            island_x: this.island_x,
            island_y: this.island_y
        });
    }
};
WndHandlerWonders.prototype.startNextBuildingPhase = function () {
    this.wnd.requestContentPost('wonders', 'start_next_building_phase', {
        island_x: this.island_x,
        island_y: this.island_y
    });
};
WndHandlerWonders.prototype.toggleInfoText = function () {
    var root = this.wnd.getJQElement();
    root.find('div.wonder_info_text').toggle();
    root.find('div.wonder_more_information').toggleClass('show hide');
    root.find('a.toggle_wonder_info_text').toggleClass('open closed');
};
WndHandlerWonders.prototype.registerEventListeners = function () {
    var that = this;
    GPEvents.global.bind(GPEvents.global.town.town_switch + '.WndHandlerWonders', function () {
        that.wnd.requestContentGet('wonders', 'index', {
            island_x: that.island_x,
            island_y: that.island_y
        });
    });
};
WndHandlerWonders.prototype.unregisterEventListeners = function () {
    GPEvents.global.unbind('.WndHandlerWonders');
};

function WndHandlerAllianceForum(wndhandle) {
    this.currentTownID = -1;
    this.wnd = wndhandle;
}
WndHandlerAllianceForum.inherits(AbstractWndHandlerEmailValidation);
WndHandlerAllianceForum.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        maxHeight: 520,
        maxWidth: 780,
        height: 520,
        width: 780,
        resizable: false,
        minimizable: true,
        menuScroll: true,
        title: 'Alliance forum'
    };
    return ret;
};
WndHandlerAllianceForum.prototype.onInit = function (title, UIopts) {
    this.wnd.sendMessage('openIndex', {}, function () {
        Forum.displayData();
    });
    $('#new_post.new_post').toggleClass('new_post no_new_post');
    $('#link_alliance_forum').removeClass('new_post');
    return true;
};
WndHandlerAllianceForum.prototype.onClose = function () {
    Layout.allianceForum.close(true);
    return true;
};
WndHandlerAllianceForum.prototype.onRcvData = function (data) {
    if (data.html) {
        var html = this.getTransformedHtml(data.html);
        this.wnd.setContent2(html);
        this.initializeBBCodes();
        Forum.initialize();
    }
};
WndHandlerAllianceForum.prototype.onSetContent = function (html) {
    return this.getTransformedHtml(html);
};
WndHandlerAllianceForum.prototype.parentOnMessage = WndHandlerAllianceForum.prototype.onMessage;
WndHandlerAllianceForum.prototype.onMessage = function (type, data, callback) {
    var threads_per_page = Forum.getThreadsPerPage();
    var base_params = (threads_per_page !== undefined) ? {
        threads_per_page: threads_per_page
    } : {};
    base_params.type = type;
    base_params.separate = Forum.flag_separate_forum_tab;
    if (type === 'openIndex') {
        this.wnd.clearMenu();
        this.wnd.requestContentGet('alliance_forum', 'forum', base_params, callback);
    } else if (type === 'go') {
        this.wnd.clearMenu();
        this.wnd.requestContentPost('alliance_forum', 'forum', $.extend(base_params, data), callback);
    } else if (type === 'switchForum') {
        Forum.switchForum(data);
    } else {
        return this.parentOnMessage.apply(this, (arguments));
    }
};
WndHandlerAllianceForum.prototype.getTransformedHtml = function (html) {
    var transformed_html = '<div id="content" class="forum_content">' + html + '<script type="text/javascript">GPEvents.global.trigger(GPEvents.global.forum.contents_set);</script></div>';
    transformed_html += '<a href="/forum" target="_blank" class="separate_forum_tab_link">' + 'Maximize' + '</a>';
    return transformed_html;
};
WndHandlerAllianceForum.prototype.initializeBBCodes = function () {
    var wrapper = this.wnd.getJQElement().find('div.bb_button_wrapper');
    this.bbcode = new BBCode(this.wnd, wrapper, '#forum_post_textarea');
};
WndHandlerAllianceForum.prototype.validateEmail = function () {
    var that = this;
    var params = {};
    params.code = that.wnd.getJQElement().find('#validate_form [name=code]').val();
    this.wnd.ajaxRequestPost('player', 'validate_email', params, function (window, data) {
        $('#emailbar').remove();
        that.wnd.sendMessage('openIndex');
    });
};

function WndHandlerAllianceProfile(wndhandle) {
    this.wnd = wndhandle;
}
WndHandlerAllianceProfile.inherits(WndHandlerDefault);
WndHandlerAllianceProfile.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        maxHeight: 900,
        maxWidth: 1200,
        height: 650,
        width: 790,
        resizable: true,
        title: 'Alliance'
    };
    return ret;
};
WndHandlerAllianceProfile.prototype.onInit = function (title, UIopts) {
    this.wnd.requestContentGet('alliance', 'profile', {
        'alliance_id': UIopts.alliance_id
    });
    return true;
};
WndHandlerAllianceProfile.prototype.onClose = function () {
    return true;
};
WndHandlerAllianceProfile.prototype.onRcvData = function (data) {
    this.wnd.setContent2(data.html);
};
WndHandlerAllianceProfile.prototype.pactInvite = function (alliance) {
    this.wnd.ajaxRequestPost('alliance', 'pact_invite', {
        name: alliance
    }, function (_wnd, data) {});
};

function WndHandlerAttackPlaner(wndhandle) {
    this.wnd = wndhandle;
    this.pre_selected_target_id = 0;
}
WndHandlerAttackPlaner.inherits(WndHandlerDefault);
WndHandlerAttackPlaner.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        height: 500,
        width: 750,
        resizable: false,
        minimizable: true,
        help: true,
        title: 'Untitled Window'
    };
    return ret;
};
WndHandlerAttackPlaner.prototype.onInit = function (title, UIopts, action, params) {
    action = action || 'index';
    params = params || {};
    this.wnd.requestContentGet('attack_planer', action, params);
    return true;
};
WndHandlerAttackPlaner.prototype.onRcvData = function (data) {
    var that = this;
    this.root = this.wnd.getJQElement();
    this.wnd.setContent2(data.html);
    this.wnd.setTitle('Attack planner');
    $('#ap_sort_by_start').click(function () {
        that.sortAttacks('ot.send_at');
    });
    $('#ap_sort_by_arrival').click(function () {
        that.sortAttacks('ot.arrival_at');
    });
};
WndHandlerAttackPlaner.prototype.createPlan = function () {
    var name = this.root.find('#plan_name').val();
    var description = this.root.find('#description').val();
    this.wnd.clearMenu();
    this.wnd.requestContentPost('attack_planer', 'create_plan', {
        name: name,
        description: description
    });
};
WndHandlerAttackPlaner.prototype.showPlan = function (plan_id, back) {
    var that = this;
    this.wnd.ajaxRequestGet('attack_planer', 'show_plan', {
        plan_id: plan_id
    }, function (window, data) {
        if (back) {
            data.html = data.html.replace('ui-selected', '');
            data.html = data.html.replace('<li class="target_even target ' + that.pre_selected_target_id, '<li class="target_even target ' + that.pre_selected_target_id + ' ui-selected');
            data.html = data.html.replace('<li class="target_odd target ' + that.pre_selected_target_id, '<li class="target_odd target ' + that.pre_selected_target_id + ' ui-selected');
        }
        that.wnd.setContent2(data.html);
        var elements = $('a.submenu_link');
        var j = elements.length;
        while (j--) {
            $(elements[j]).removeClass('active');
        }
        that.wnd.setTitle('Attack planner' + ' - ' + data.plan_name);
    });
    if (back) {
        this.fetchAttacksForTarget(plan_id, this.pre_selected_target_id);
    }
};
WndHandlerAttackPlaner.prototype.deletePlan = function (plan_id) {
    var plan_count = this.root.find('table.game_table tr').length;
    this.wnd.clearMenu();
    this.wnd.requestContentPost('attack_planer', 'delete_plan', {
        plan_id: plan_id,
        plan_count: plan_count - 1
    });
};
WndHandlerAttackPlaner.prototype.showTargetDialog = function () {
    var add_target = this.root.find('div.add_target');
    if (add_target.hasClass('inactive')) {
        add_target.removeClass('inactive').addClass('active');
        this.root.find('a.add_entry.target').hide();
    } else {
        add_target.addClass('inactive').removeClass('active');
    }
};
WndHandlerAttackPlaner.prototype.addTarget = function (plan_id) {
    var that = this;
    if (this.root.find('input.target_name').val() == '') {
        this.root.find('div.add_target').addClass('inactive').removeClass('active');
        this.root.find('a.add_entry.target').show();
        return;
    }
    var target_id = ~~ (this.root.find('input.target_id').val());
    this.wnd.ajaxRequestPost('attack_planer', 'add_target', {
        plan_id: plan_id,
        target_id: target_id
    }, function (window, data) {
        that.wnd.setContent2(data.html);
    });
};
WndHandlerAttackPlaner.prototype.showAttackDialog = function (plan_id) {
    var that = this;
    if (this.root.find('li.target.ui-selected').length == 0) {
        HumanMessage.error('You have to specify a target for your attack first.');
        return;
    }
    var target_id = ~~ (this.root.find('li.target.ui-selected')[0].className.match(/\d+/)[0]);
    this.pre_selected_target_id = target_id;
    this.showAjaxLoader();
    this.wnd.ajaxRequestGet('attack_planer', 'show_attack_dialog', {
        plan_id: plan_id,
        target_id: target_id,
        order_by: 'names'
    }, function (window, data) {
        that.wnd.setContent2(data.html);
        that.img.parentNode.removeChild(that.img);
    });
};
WndHandlerAttackPlaner.prototype.addOriginTown = function (plan_id, target_id) {
    var that = this;
    var units = {};
    var send_at = 0;
    var naval = false;
    var type = this.root.find('div.support_filter.inactive');
    var origin_town_id = ~~ (this.root.find('li.ui-selected input.origin_town_id').val());
    var arrival_at = this.getArrivalAtFromInputFields();
    type = ($(type).hasClass('support') ? 'attack' : 'support');
    var foo, val;
    $('li.ui-selected input.unit').each(function () {
        foo = $(this);
        if ((val = ~~ (foo.val())) !== 0 && foo.hasClass('naval')) {
            naval = true;
        }
        this.className.match(/unit_(\w+)/);
        units[RegExp.$1] = val;
    });
    $('li.ui-selected input.duration' + (naval ? '.naval' : '') + '.hidden').each(function () {
        this.className.match(/unit_(\w+)/);
        if (that.root.find('li.ui-selected input.unit.unit_' + RegExp.$1).val() != 0) {
            send_at = Math.max(~~ ($(this).val()), send_at);
        }
    });
    this.wnd.ajaxRequestPost('attack_planer', 'add_origin_town', {
        plan_id: plan_id,
        target_id: target_id,
        send_at: send_at,
        origin_town: origin_town_id,
        type: type,
        units: units,
        arrival_at: arrival_at
    }, function (window, data) {
        data.html = data.html.replace('ui-selected', '');
        data.html = data.html.replace('<li class="target_even target ' + target_id, '<li class="target_even target ' + target_id + ' ui-selected');
        data.html = data.html.replace('<li class="target_odd target ' + target_id, '<li class="target_odd target ' + target_id + ' ui-selected');
        that.wnd.setContent(data.html);
        that.fetchAttacksForTarget(plan_id, target_id);
    });
};
WndHandlerAttackPlaner.prototype.editAttackDialog = function () {
    var that = this;
    var data_id = ~~ (this.root.find('li.origin_town.ui-selected')[0].className.match(/\d+/)[0]);
    this.pre_selected_target_id = ~~ (this.root.find('ul.target_list li.target.ui-selected')[0].className.match(/\d+/)[0]);
    this.wnd.ajaxRequestGet('attack_planer', 'edit_origin_town_dialog', {
        data_id: data_id
    }, function (window, data) {
        that.wnd.setContent2(data.html);
    });
};
WndHandlerAttackPlaner.prototype.getArrivalAtFromInputFields = function () {
    var arrivalDate = newDateByTimezone(parseInt($('#attack_year').val(), 10), parseInt($('#attack_month').val(), 10) - 1, parseInt($('#attack_day').val(), 10), parseInt($('#attack_hour').val(), 10), parseInt($('#attack_min').val(), 10), parseInt($('#attack_sec').val(), 10), Timestamp.serverGMTOffset());
    return Math.round(arrivalDate.getTime() * 0.001);
};
WndHandlerAttackPlaner.prototype.editOriginTown = function (plan_id, target_id, data_id, origin_town_id) {
    var that = this;
    var units = {};
    var type = this.root.find('div.support_filter.inactive');
    var arrival_at = this.getArrivalAtFromInputFields();
    var elements = $('input.unit');
    var j = elements.length;
    type = ($(type).hasClass('support') ? 'attack' : 'support');
    while (j--) {
        elements[j].className.match(/unit_(\w+)/);
        units[RegExp.$1] = ~~ ($(elements[j]).val());
    }
    this.wnd.ajaxRequestPost('attack_planer', 'edit_origin_town', {
        data_id: data_id,
        plan_id: plan_id,
        target_id: target_id,
        type: type,
        origin_town: origin_town_id,
        units: units,
        arrival_at: arrival_at
    }, function (window, data) {
        data.html = data.html.replace('ui-selected', '');
        data.html = data.html.replace('<li class="target_even target ' + target_id, '<li class="target_even target ' + target_id + ' ui-selected');
        data.html = data.html.replace('<li class="target_odd target ' + target_id, '<li class="target_odd target ' + target_id + ' ui-selected');
        that.wnd.setContent(data.html);
        that.fetchAttacksForTarget(plan_id, target_id);
    });
};
WndHandlerAttackPlaner.prototype.deleteOriginTown = function (plan_id) {
    var that = this;
    if (this.root.find('li.origin_town.ui-selected').length == 0) {
        HumanMessage.error('You have to select an attack.');
        return;
    }
    var id_to_delete = ~~ (this.root.find('li.origin_town.ui-selected')[0].className.match(/\d+/)[0]);
    this.wnd.ajaxRequestPost('attack_planer', 'delete_origin_town', {
        plan_id: plan_id,
        id: id_to_delete
    }, function (window, data) {
        that.root.find('li.origin_town.ui-selected').remove();
        if (that.root.find('ul.attacks_list').children().length <= 0) {
            that.root.find('ul.attacks_list').append('<li class="small" style="line-height:25px;">' + 'You haven\'t entered any attacks for this target yet.' + '</li>');
        }
        Layout.deleteAttackPlanerNotifications(data.id);
    });
};
WndHandlerAttackPlaner.prototype.deleteTarget = function (plan_id) {
    var that = this;
    var target_id = ~~ (this.root.find('li.target.ui-selected')[0].className.match(/\d+/)[0]);
    this.wnd.ajaxRequestPost('attack_planer', 'delete_target', {
        plan_id: plan_id,
        target_id: target_id
    }, function (window, data) {
        that.wnd.setContent2(data.html);
    });
};
WndHandlerAttackPlaner.prototype.fetchAttacksForTarget = function (pre_plan_id, pre_target_id) {
    var that = this;
    var plan_id = pre_plan_id ? pre_plan_id : ~~ (this.root.find('div.plan_header')[0].className.match(/\d+/)[0]);
    var target_id = pre_target_id ? pre_target_id : ~~ (this.root.find('li.target.ui-selected')[0].className.match(/\d+/)[0]);
    this.showAjaxLoader();
    this.wnd.ajaxRequestGet('attack_planer', 'fetch_origin_towns_for_target', {
        plan_id: plan_id,
        target_id: target_id
    }, function (window, data) {
        $('#attack_plan_origin_town_list').html(data.html);
        that.root.find('ul.attacks_list').selectable();
        that.img.parentNode.removeChild(that.img);
    });
};
WndHandlerAttackPlaner.prototype.addRightsToPlan = function (plan_id, visibility_type) {
    var that = this;
    var visibility_name = '';
    if (visibility_type == 'alliance') {
        visibility_name = this.root.find('input.alliance_name.plan_' + plan_id).val();
    } else {
        visibility_name = this.root.find('input.player_name.plan_' + plan_id).val();
    }
    this.wnd.ajaxRequestPost('attack_planer', 'add_rights_to_plan', {
        plan_id: plan_id,
        visibility_name: visibility_name,
        visibility_type: visibility_type
    }, function (window, data) {
        that.wnd.setContent2(data.html);
    });
};
WndHandlerAttackPlaner.prototype.deleteRightsFromPlan = function (plan_id, visibility_id, visibility_type) {
    var that = this;
    this.wnd.ajaxRequestPost('attack_planer', 'delete_rights_from_plan', {
        plan_id: plan_id,
        visibility_id: visibility_id,
        visibility_type: visibility_type
    }, function (window, data) {
        that.wnd.setContent2(data.html);
    });
};
WndHandlerAttackPlaner.prototype.editRights = function (plan_id) {
    var that = this;
    this.wnd.ajaxRequestGet('attack_planer', 'rights', {
        plan_id: plan_id
    }, function (window, data) {
        that.wnd.setContent2(data.html);
        that.wnd.setTitle('Attack planner' + ' - ' + data.plan_name);
    });
};
WndHandlerAttackPlaner.prototype.switchTownForAttack = function (town_id, target_id, type) {
    var units = {};
    var target_town_name = this.root.find('li.target.ui-selected span.town_name a').text();
    var elements = $('li.origin_town.ui-selected div.place_unit');
    var j = elements.length;
    while (j--) {
        elements[j].className.match(/unit_(\w+)/);
        units[RegExp.$1] = ~~ ($(elements[j]).find('span.attack_plan_white').text());
    }
    Layout.townSwitch(town_id);
    Layout.wnd.Create(Layout.wnd.TYPE_TOWN, s(type == 'attack' ? '%1 angreifen' : '%1 unterstützen', target_town_name), {
        action: type
    }, {
        id: target_id,
        preselect: true,
        preselect_units: units
    });
};
WndHandlerAttackPlaner.prototype.sortAttacks = function (sort) {
    this.wnd.requestContentGet('attack_planer', 'attacks', {
        sort: sort
    });
};
WndHandlerAttackPlaner.prototype.sortOriginTownsBy = function (sort, plan_id, target_id) {
    var that = this;
    this.showAjaxLoader();
    this.wnd.ajaxRequestGet('attack_planer', 'show_attack_dialog', {
        plan_id: plan_id,
        target_id: target_id,
        order_by: sort
    }, function (window, data) {
        that.wnd.setContent2(data.html);
        that.img.parentNode.removeChild(that.img);
    });
};
WndHandlerAttackPlaner.prototype.selectUnit = function (elm) {
    elm = $(elm);
    var unit_count = ~~ (elm.find('span.attack_plan_black').html());
    var prev = ~~elm.next().find('input').val();
    elm.next().find('input').val(prev === unit_count ? 0 : unit_count);
};
WndHandlerAttackPlaner.prototype.showHelp = function () {
    var title = 'Attack planner' + '-' + 'Information';
    var text = 'You can create new plans under <b>New Plan</b>.' + '<br />' + '<b>Tip:</b> It\'s a good idea to create plans with appropriate titles and descriptions to give you a clear overview of your battle strategies.' + '<ul class="temple_list"><li>' + 'You can add as many targets as you wish to each plan.' + '</li><li>' + 'You can add as many attacks as you wish to each target.' + '</li><li>' + 'When you plan attacks, all of the required travel times will be displayed to you.' + '</li><li>' + 'You will be notified of impending attacks.' + '</li><li>' + 'Share your plans with other players and alliances. (The plans are only visible to players with a captain)' + '</li><li>' + 'You can start your planned attacks directly from the plan.' + '</li></ul>' + '\'Attacks\' will give you an overview of all attacks that you or your allies have planned. You can also sort them by the arrival or departure of your troops.';
    Layout.dialogWindow.open(text, title, 400, 350);
};
WndHandlerAttackPlaner.prototype.showAjaxLoader = function () {
    var img = document.createElement('img');
    img.id = 'ajax_loader';
    img.src = "http://cdn.grepolis.com/images/game/ajax-loader.gif";
    img.alt = '';
    img.style.top = ((this.root.height() >> 1) - 29) + 'px';
    img.style.left = ((this.root.width() >> 1) - 29) + 'px';
    this.root.append(img);
    this.img = img;
};

function WndHandlerMessage(wndhandle) {
    this.wnd = wndhandle;
    this.recipients = null;
    this.bbcode = null;
    this.last_folder_id = 0;
    this.last_messages_ids = null;
}
WndHandlerMessage.inherits(AbstractWndHandlerEmailValidation);
WndHandlerMessage.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        height: 600,
        width: 800,
        resizable: false,
        minimizable: true,
        title: 'Untitled Window'
    };
    return ret;
};
WndHandlerMessage.prototype.onInit = function (title, UIopts, action, recipients, id) {
    this.recipients = recipients || null;
    if (!action) {
        action = 'index';
    }
    if (id == undefined) {
        this.wnd.requestContentGet('message', action, {});
    } else {
        this.wnd.requestContentGet('message', action, {
            id: id
        });
    }
    return true;
};

function getSelectedMessages() {
    var counter = 0,
        parent = $('<div></div>');
    $("#message_list .message_date input:checked").each(function () {
        parent.append($(this).parent().parent().clone());
        counter++;
    });
    return [counter, parent];
}

function removeSelectedMessages() {
    $("#message_list .message_date input:checked").each(function () {
        $(this).parent().parent().remove();
    });
}

function getMessagesIds() {
    var values = [];
    $("#message_list .message_date input:checked").each(function () {
        values[values.length] = $(this).val();
    });
    return values;
}
WndHandlerMessage.prototype.onRcvData = function (data, controller, action) {
    var html = document.createElement('div'),
        wrapper, that = this;
    html.innerHTML = data.html;
    wrapper = $(html).find('div.bb_button_wrapper');
    this.wnd.clearMenu();
    this.wnd.setContent2(html);
    switch (action) {
    case 'forward':
        this.bbcode = new BBCode(this.wnd, wrapper, '#message_message');
        break;
    case 'new':
        this.bbcode = new BBCode(this.wnd, wrapper, '#message_new_message');
        break;
    case 'view':
        this.bbcode = new BBCode(this.wnd, wrapper, '#message_reply_message');
        $('#message-index').addClass('active');
        break;
    case 'create':
        break;
    }
    if (this.recipients != null) {
        this.wnd.sendMessage('setRecipients', this.recipients);
    }
    if ((action == "index" || action == "move" || action == "create") && data.has_curator == "1") {
        if (action == "move") {
            Message.toggleMenu();
        }
        $('#message_list li.message_item').draggable2({
            distance: 20,
            live: false,
            helper: function (obj, e) {
                if (e.target.tagName !== "INPUT") {
                    $(this).find('input').attr('checked', 'checked');
                }
                var selected = getSelectedMessages(),
                    length = selected[0];
                return length > 1 ? $('<div class="multidragging"><div class="header">' + length + ' ' + "Messages" + '</div></div>').append(selected[1]).css({
                    width: $(this).width()
                }) : $(this).clone().css({
                    width: $(this).width()
                });
            }
        });
        $('#folder_menu_messages span.folder').droppable2({
            'drop': function (params) {
                var folder_id = $(this).attr('name');
                folder_id = folder_id.split("_")[1];
                var messages_ids = getMessagesIds();
                if (that.last_folder_id !== folder_id || that.last_messages_ids !== messages_ids) {
                    that.wnd.sendMessage('messageMove', 'message_form', folder_id, that.last_folder_id);
                    that.last_messages_ids = messages_ids;
                    removeSelectedMessages();
                }
            }
        });
    }
    var additional_descriptive_text = this.wnd.getJQElement().find('div.player_settings h4.additional_descriptive_text');
    if ((action == "new" || action == "resend_validation_email") && additional_descriptive_text.length) {
        additional_descriptive_text.html('You have to activate your email to send messages!');
        additional_descriptive_text.show();
    }
};
WndHandlerMessage.prototype.onSetContent = function (html) {
    return new_html;
};
WndHandlerMessage.prototype.messageNew = function (recipients) {
    this.recipients = recipients || null;
    this.wnd.requestContentGet('message', 'new', {});
};
WndHandlerMessage.prototype.messageView = function (message_id, action) {
    NotificationStack.deleteByTypeAndParamID(NotificationType.NEWMESSAGE, message_id);
    NotificationStack.deleteByTypeAndParamID(NotificationType.AWMESSAGE, message_id);
    this.wnd.requestContentGet('message', action, {
        id: message_id
    });
};
WndHandlerMessage.prototype.messageForward = function (message_id) {
    this.wnd.requestContentGet('message', 'forward', {
        id: message_id
    });
};
WndHandlerMessage.prototype.messageDeleteOne = function (message_id, action) {
    NotificationStack.deleteByTypeAndParamID(NotificationType.NEWMESSAGE, message_id);
    NotificationStack.deleteByTypeAndParamID(NotificationType.AWMESSAGE, message_id);
    this.wnd.requestContentPost('message', action, {
        id: message_id
    });
};
WndHandlerMessage.prototype.messageChangeFolder = function (folder_id) {
    this.last_folder_id = folder_id;
    this.wnd.requestContentGet('message', 'index', {
        folder_id: folder_id
    });
};
WndHandlerMessage.prototype.messageFilter = function (folder_id, status) {
    this.wnd.requestContentGet('message', 'index', {
        folder_id: folder_id,
        status: status
    });
};
WndHandlerMessage.prototype.setRecipients = function (recipients) {
    var that = this;
    that.wnd.getJQElement().find('#message_recipients').val(recipients);
};
WndHandlerMessage.prototype.submitForm = function (form_id, action) {
    var params = {};
    if (action == 'delete' || action == 'delete_massmail') {
        params.message_ids = [];
        $('#' + form_id + ' input:checkbox:checked').each(function () {
            if (~~ (this.value) > 0) {
                params.message_ids.push(this.value);
                NotificationStack.deleteByTypeAndParamID(NotificationType.NEWMESSAGE, ~~ (this.value));
                NotificationStack.deleteByTypeAndParamID(NotificationType.AWMESSAGE, ~~ (this.value));
            }
        });
    } else {
        $('#' + form_id + ' input').each(function () {
            params[this.name] = this.value;
        });
        $('#' + form_id + ' textarea').each(function () {
            params[this.name] = this.value;
        });
    }
    this.wnd.requestContentPost('message', action, params);
};
WndHandlerMessage.prototype.messageMove = function (form_id, folder_id, last_folder_id) {
    var params = {
        folder_id: folder_id,
        message_ids: []
    };
    if (typeof last_folder_id != 'undefined') {
        params.last_folder_id = last_folder_id;
    }
    $('#' + form_id + ' input:checkbox:checked').each(function () {
        params.message_ids.push(this.value);
    });
    this.wnd.requestContentPost('message', 'move', params);
};
WndHandlerMessage.prototype.messageDoReportAffront = function (message_id) {
    var params = {
        message_id: message_id,
        post_id: Message.post_id,
        reason: $('#message_report_affront_dialog textarea').val()
    };
    Layout.ajaxPost('message', 'reportAffront', params, true, function (data) {
        if (data.success) {
            Message.closeReportAffrontDialog();
        }
    });
};
WndHandlerMessage.prototype.messagePreview = function (part) {
    var params = {
        message: $('#message_' + part + '_message').val()
    };
    Layout.ajaxGet('message', 'preview', params, true, function (data) {
        $('#message_' + part + '_preview_body').html(data.message);
        $('#message_' + part + '_create').hide();
        $('#message_' + part + '_preview').show();
    });
    return false;
};
WndHandlerMessage.prototype.validateEmail = function () {
    var that = this;
    var params = {};
    params.code = that.wnd.getJQElement().find('#validate_form [name=code]').val();
    this.wnd.ajaxRequestPost('player', 'validate_email', params, function (window, data) {
        $('#emailbar').remove();
        that.wnd.requestContentGet('message', 'new', {});
    });
};

function WndHandlerReport(wndhandle) {
    this.wnd = wndhandle;
    this.current_folder_id = 0;
    this.last_folder_id = 0;
    this.last_messages_ids = null;
}
WndHandlerReport.inherits(WndHandlerDefault);
WndHandlerReport.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        minHeight: 500,
        width: 800,
        resizable: false,
        autoresize: true,
        title: 'Untitled Window'
    };
    return ret;
};
WndHandlerReport.prototype.onInit = function (title, UIopts, id, open_action) {
    open_action = open_action || 'index';
    if (id == undefined) {
        this.wnd.requestContentGet('report', open_action, {});
    } else {
        this.wnd.requestContentGet('report', open_action, {
            id: id
        });
    }
    return true;
};

function getSelectedReports() {
    var counter = 0,
        parent = $('<div></div>');
    $("#report_list .reports_date input:checked").each(function () {
        parent.append($(this).parent().parent().clone());
        counter++;
    });
    return [counter, parent];
}

function removeSelectedReports() {
    $("#report_list .reports_date input:checked").each(function () {
        $(this).parent().parent().remove();
    });
}

function getReportsIds() {
    var values = [];
    $("#report_list .reports_date input:checked").each(function () {
        values[values.length] = $(this).val();
    });
    return values;
}
WndHandlerReport.prototype.onRcvData = function (data, controller, action) {
    var that = this;
    this.wnd.setContent(data.html);
    if ((action == "index" || action == "move") && data.has_curator == "1") {
        if (action == "move") {
            Reports.toggleMenu();
        }
        $('#report_list li.report_item').draggable2({
            distance: 20,
            live: false,
            helper: function (obj, e) {
                if (e.target.tagName !== "INPUT") {
                    $(this).find('input').attr('checked', 'checked');
                }
                var selected = getSelectedReports(),
                    length = selected[0];
                return length > 1 ? $('<div class="multidragging"><div class="header">' + length + ' ' + "Reports" + '</div></div>').append(selected[1]).css({
                    width: $(this).width()
                }) : $(this).clone().css({
                    width: $(this).width()
                });
            }
        });
        $('#folder_menu_reports span.folder').droppable2({
            'drop': function (params) {
                var folder_id = $(this).attr('name');
                folder_id = folder_id.split("_")[1];
                var reports_ids = getReportsIds();
                if (that.last_folder_id !== folder_id || that.last_messages_ids !== messages_ids) {
                    that.wnd.sendMessage('reportMove', 'report_form', folder_id, that.last_folder_id);
                    that.last_messages_ids = messages_ids;
                    removeSelectedReports();
                }
            }
        });
    }
    if (action == 'view' && $('a#cultural_points_link').length) {
        $('a#cultural_points_link').unbind('click').bind('click', function () {
            Layout.buildingWindow.open('place');
        });
    }
};
WndHandlerReport.prototype.reportView = function (report_id) {
    NotificationStack.deleteByTypeAndParamID(NotificationType.NEWREPORT, report_id);
    this.wnd.requestContentGet('report', 'view', {
        id: report_id
    });
};
WndHandlerReport.prototype.reportDeleteOne = function (report_id) {
    NotificationStack.deleteByTypeAndParamID(NotificationType.NEWREPORT, report_id);
    this.wnd.requestContentPost('report', 'delete', {
        report_id: report_id,
        folder_id: this.current_folder_id
    });
};
WndHandlerReport.prototype.reportChangeFolder = function (folder_id) {
    this.current_folder_id = folder_id;
    this.last_folder_id = folder_id;
    this.wnd.requestContentGet('report', 'index', {
        folder_id: folder_id
    });
};
WndHandlerReport.prototype.reportDeleteMany = function (form_id) {
    var params = {};
    params.folder_id = this.current_folder_id;
    params.report_ids = [];
    $('#' + form_id + ' input:checkbox:checked').each(function () {
        params.report_ids.push(this.value);
        NotificationStack.deleteByTypeAndParamID(NotificationType.NEWREPORT, ~~ (this.value));
    });
    this.wnd.requestContentPost('report', 'delete_many', params);
};
WndHandlerReport.prototype.reportDeleteAllOfFolder = function () {
    var params = {
        folder_id: this.current_folder_id
    },
        that = this,
        nStack = NotificationStack,
        reports_ids, notification;
    if (!Tutorial || !Tutorial.isActive()) {
        that.wnd.requestContentPost('report', 'delete_all_from_folder', params, function (wndObj, data, status) {
            if (status == "success") {
                reports_ids = data.reports_ids ? data.reports_ids : [];
                that.wnd.requestContentPost('report', 'delete_many', {
                    folder_id: that.current_folder_id,
                    report_ids: reports_ids
                }, function (wndObj, data, status) {
                    if (status == "success") {
                        nStack.loop(function (i, elem, arr) {
                            if (jQuery.inArray(elem.getReportId(), reports_ids) > -1) {
                                elem.destroy();
                                arr.remove(i);
                            }
                        });
                    }
                });
            }
        });
    } else {
        HumanMessage.error('Sorry, this action isn\'t possible, please finish the tutorial first');
    }
};
WndHandlerReport.prototype.resourceReportDeleteMany = function (form_id) {
    var params = {};
    params.resource_transport_report_ids = [];
    $('#' + form_id + ' input:checkbox:checked').each(function () {
        params.resource_transport_report_ids.push(this.value);
    });
    this.wnd.requestContentPost('report', 'delete_many_res_trans_reports', params);
};
WndHandlerReport.prototype.resourceReportDeleteAll = function () {
    this.wnd.requestContentPost('report', 'delete_all_res_trans_reports', {});
};
WndHandlerReport.prototype.reportMove = function (form_id, folder_id, last_folder_id) {
    var params = {
        folder_id: folder_id
    };
    params.report_ids = [];
    if (typeof last_folder_id != 'undefined') {
        params.last_folder_id = last_folder_id;
    }
    this.current_folder_id = folder_id;
    $('#' + form_id + ' input:checkbox:checked').each(function () {
        params.report_ids.push(this.value);
    });
    this.wnd.requestContentPost('report', 'move', params);
};
WndHandlerReport.prototype.reportMoveOne = function (report_id, folder_id) {
    Layout.ajaxPost('report', 'move_report', {
        report_id: report_id,
        folder_id: folder_id
    }, true, function () {});
};
WndHandlerReport.prototype.unpublishReportMany = function (form_id) {
    var params = {};
    params.report_ids = [];
    $('#' + form_id + ' input:checkbox:checked').each(function () {
        params.report_ids.push(this.value);
    });
    this.wnd.requestContentPost('report', 'unpublish_report_many', params);
};
WndHandlerReport.prototype.loadIndexTab = function () {
    this.wnd.requestContentGet('report', 'index', {
        folder_id: this.current_folder_id
    });
};

function WndHandlerPublishReport(wndhandle) {
    this.wnd = wndhandle;
}
WndHandlerPublishReport.inherits(WndHandlerDefault);
WndHandlerPublishReport.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        height: 350,
        width: 400,
        resizable: false,
        title: 'Untitled Window'
    };
    return ret;
}
WndHandlerPublishReport.prototype.onInit = function (title, UIopts) {
    var index_controller = 'report';
    var report_id = UIopts;
    this.wnd.requestContentGet(index_controller, 'publish_report_dialog', {
        report_id: report_id
    })
    return true;
};
WndHandlerPublishReport.prototype.onRcvData = function (data) {
    this.wnd.setContent2(data.html);
    var root = this.wnd.getJQElement();
    root.find('#publish_report_show_all').click(function (evt) {
        root.find('#publish_report_options input').attr('checked', $(evt.target).attr('checked'));
    });
    root.find('#publish_report_options input').click(function (evt) {
        root.find('#publish_report_show_all').attr('checked', false);
    })
};
WndHandlerPublishReport.prototype.unpublishReport = function () {
    var params = {};
    $('#publish_report_dialog_form input[type="hidden"]').each(function (idx, elm) {
        params[elm.name] = $(elm).val();
    });
    this.wnd.ajaxRequestPost('report', 'unpublish_report', params, function (_wnd, data) {
        _wnd.close();
    }, {});
    return false;
};

function WndHandlerDialog(wndhandle) {
    this.wnd = wndhandle;
    this.onCloseFunction = null;
}
WndHandlerDialog.inherits(WndHandlerDefault);
WndHandlerDialog.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        height: 250,
        width: 400,
        resizable: false,
        autoresize: true,
        title: ''
    };
    return ret;
};
WndHandlerDialog.prototype.onInit = function (title, options) {
    if (options && typeof options.onClose == 'function') {
        this.onCloseFunction = options.onClose;
    }
    return true;
};
WndHandlerDialog.prototype.onRcvData = function (data, controller, action) {
    this.wnd.setContent(data.html);
};
WndHandlerDialog.prototype.onMessage = function () {
    return null;
};
WndHandlerDialog.prototype.onClose = function () {
    if (typeof this.onCloseFunction == 'function') {
        return this.onCloseFunction();
    }
    return true;
};

function WndHandlerConfirmDialog(wndhandle) {
    this.wnd = wndhandle;
    var that = this,
        checked = false,
        f;
    this.clickHandler = function (e) {
        var target = e.target.tagName === 'A' ? $(e.target) : $(e.target).closest('a.button'),
            href = (target.attr('href') || '').split(/#/).reverse()[0];
        switch (href) {
        case 'confirm':
            that.executeCallback('confirm');
            that.wnd.close();
            break;
        case 'cancel':
            that.executeCallback('cancel');
            that.wnd.close();
            break;
        case 'checkbox':
            that.executeCallback('check');
            target.toggleClass('checked', (checked = !checked));
            Layout.ajaxPost('player', 'toggle_confirmation_popups', {
                'show': checked ? '0' : '1'
            }, true, function () {
                Layout.show_confirmation_popup = !checked;
            });
            break;
        }
    };
    this.setCallback = function (_f) {
        f = _f;
    };
    this.executeCallback = function (type) {
        if (f[type] && typeof f[type] === 'function') {
            f[type]();
        }
    };
}
WndHandlerConfirmDialog.inherits(WndHandlerDefault);
WndHandlerConfirmDialog.prototype.getDefaultWindowOptions = function () {
    var ret = {
        width: 400,
        autoresize: true
    };
    return ret;
};
WndHandlerConfirmDialog.prototype.onRcvData = function (data) {
    var that = this,
        callback = data.callback || {};
    if (typeof callback.confirm === String) {
        data.texts.onConfirm = callback.confirm;
    } else {
        data.texts.onConfirm = false;
    }
    this.setCallback({
        'confirm': callback.confirm,
        'cancel': callback.cancel,
        'check': callback.check
    });
    this.wnd.setContent(tmpl2(GameData.ConfirmDialogTemplate, data.texts));
    this.wnd.getJQElement().click(that.clickHandler);
};
WndHandlerConfirmDialog.prototype.onClose = function () {
    this.executeCallback('cancel');
    return true;
};

WndHandlerUninhabited = function (wndhandle) {
    this.wnd = wndhandle;
    this.params;
};
WndHandlerUninhabited.inherits(WndHandlerAttack);
WndHandlerUninhabited.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        height: 500,
        width: 500,
        resizable: false,
        title: 'Untitled Window'
    };
    return ret;
}
WndHandlerUninhabited.prototype.onInit = function (title, UIopts) {
    this.params = arguments[2];
    this.wnd.requestContentGet('uninhabited_place_info', 'info', arguments[2]);
    return true;
};
WndHandlerUninhabited.prototype.onClose = function () {
    this.unregisterEventListeners();
    return true;
};
WndHandlerUninhabited.prototype.render = function () {
    var html = tmpl2(GameData.ColonizeTemplate, this.data)
    delete this.unitInputs;
    this.wnd.setContent(html);
    var that = this;
    this.root = this.wnd.getJQElement();
    this.root.find('a.index_unit').click(function () {
        that.selectUnit(this);
    });
    this.naval();
    this.bindDurationCounter();
    this.bindCapacityCounter();
};
WndHandlerUninhabited.prototype.onRcvData = function (data) {
    this.unregisterEventListeners();
    data.json.type = '';
    this.data = data.json;
    this.data.preselect_units = this.data.preselect_units || false;
    this.duration = data.json.duration;
    var units = {};
    for (var i in data.json.units) {
        units[i] = data.json.units[i].count;
    }
    ITowns.getTown(Game.townId).units(units);
    if (data.tmpl) GameData.add({
        'ColonizeTemplate': data.tmpl
    });
    this.render();
    this.registerEventListeners();
}
WndHandlerUninhabited.prototype.sendColonizer = function () {
    var that = this;
    this.getUnitInputs().each(function () {
        var name = this.name,
            value = ~~this.value;
        if (name && value) {
            that.params[name] = value;
        }
    });
    this.wnd.ajaxRequestPost('uninhabited_place_info', 'send_colonizer', this.params, function (data) {}.bind(this), {}, 'send_colonizer');
}
WndHandlerUninhabited.prototype.bindDurationCounter = function () {
    var that = this;
    var elm = {};
    elm.root = that.wnd.getJQElement();
    elm.error = elm.root.find('div.duration_error');
    elm.duration = elm.root.find('span.way_duration');
    elm.arrival = elm.root.find('span.arrival_time');
    elm.colonize = elm.root.find('input[name="colonize_ship"]').val(ITowns.getTown(Game.townId).units().colonize_ship ? 1 : 0);

    function recalcDuration() {
        if (!~~elm.colonize.val()) {
            elm.error.show().text('You have to select a colony ship.');
            elm.duration.hide();
            elm.arrival.hide();
        } else {
            elm.error.hide();
            elm.duration.text(readableSeconds(that.duration)).show();
            elm.arrival.hide().text(that.duration).updateTime().show();
        }
    }
    recalcDuration();
    elm.root.find('a.index_unit').bind('click', recalcDuration);
    this.getUnitInputs().bind('keyup change', recalcDuration);
};
WndHandlerUninhabited.prototype.registerEventListeners = function () {
    var that = this;
    GPEvents.global.bind(GPEvents.global.town.units_changed + '.WndHandlerUninhabited' + this.wnd.getID(), function (e) {
        that.handleEvents(e, that)
    });
};
WndHandlerUninhabited.prototype.unregisterEventListeners = function () {
    GPEvents.global.unbind('.WndHandlerUninhabited' + this.wnd.getID());
};
WndHandlerUninhabited.prototype.handleEvents = function (event, that) {
    if (event.type == GPEvents.global.town.units_changed) {
        if (that.data) {
            var units = ITowns.getTown(Game.townId).units();
            for (var unit_name in that.data.units) {
                if (!that.data.units.hasOwnProperty(unit_name)) {
                    continue;
                }
                that.data.units[unit_name].count = units[unit_name];
            }
            that.render.apply(that, []);
        }
    }
};

function WndHandlerMemo(wndhandle) {
    this.wnd = wndhandle;
    this.elm = {};
}
WndHandlerMemo.inherits(WndHandlerDefault);
WndHandlerMemo.prototype.getDefaultWindowOptions = function () {
    var position = ['center', 'center'],
        cookie_pos = null;
    if ((cookie_pos = $.cookie('memo_popup_position')) && cookie_pos.indexOf('_') > 0) {
        var cookie_position = cookie_pos.split('_');
        position[0] = ~~cookie_position[0];
        position[1] = ~~cookie_position[1];
    }
    var ret = {
        'position': position,
        'height': 400,
        'width': 560,
        'title': 'Notes',
        'dragStop': function () {
            var d = this.parentNode,
                position = parseInt(d.style.left) + '_' + parseInt(d.style.top);
            $.cookie('memo_popup_position', position);
        }
    };
    return ret;
};
WndHandlerMemo.prototype.onInit = function (title, UIopts) {
    return true;
};
WndHandlerMemo.prototype.onMessage = function (type) {
    if (type == 'setData') {
        this.onRcvData(arguments[1]);
    } else if (type == 'edit') {
        this.edit();
    } else if (type == 'save') {
        this.save();
    } else if (type == 'cancel') {
        this.cancel();
    }
    return null;
}
WndHandlerMemo.prototype.onRcvData = function (data) {
    if (data.html) {
        this.wnd.setContent2(data.html)
    } else if (data.memo_html) {
        this.elm.text.html(data.memo_html);
        this.cancel();
    }
    this.elm.text = this.elm.text || $('#memo_preview_text');
    this.elm.edit = this.elm.edit || $('#memo_edit');
    this.elm.pre = this.elm.pre || $('#memo_preview');
}
WndHandlerMemo.prototype.save = function () {
    var memo_text = $('#memo_text_area').val();
    this.wnd.requestContentPost('player_memo', 'save_memo_content', {
        memo_text: memo_text
    });
}
WndHandlerMemo.prototype.cancel = function () {
    this.elm.edit.hide();
    this.elm.pre.fadeIn();
};
WndHandlerMemo.prototype.edit = function () {
    var wrapper = this.wnd.getJQElement().find('div.bb_button_wrapper');
    if (!this.bbcode) {
        this.bbcode = new BBCode(this.wnd, wrapper, '#memo_text_area');
    }
    this.elm.pre.hide();
    this.elm.edit.show();
};

function WndHandlerPremium(wndhandle) {
    this.wnd = wndhandle;
    this.subContent = {};
}
WndHandlerPremium.inherits(WndHandlerDefault);
WndHandlerPremium.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        height: 621,
        width: 790,
        resizable: false,
        title: 'Untitled Window'
    };
    return ret;
};
WndHandlerPremium.prototype.onInit = function (title, UIopts) {
    var sub_content;
    if (UIopts.sub_content != null && UIopts.sub_content != undefined) {
        sub_content = UIopts.sub_content;
    } else {
        sub_content = 'index';
    }
    this.subContent.action = sub_content;
    this.subContent.sub_tab = UIopts.sub_tab;
    if (sub_content === 'mentoring') {
        this.wnd.requestContentGet('mentoring', 'index', {});
    } else {
        this.wnd.requestContentGet('premium_features', sub_content, {});
    }
    return true;
};
WndHandlerPremium.prototype.onRcvData = function (data) {
    this.wnd.setContent2(data.html);
    if (this.subContent.sub_tab) {
        this.premiumFeature(this.subContent.sub_tab);
    }
    this.rebindOnClickEvents();
};
WndHandlerPremium.prototype.extendFeature = function (e, feature_type, auto_extend) {
    var that = this,
        disabled = 'disabled',
        button, params = {
            'feature_type': feature_type
        };
    if (e) {
        button = $(e.target);
        if (button.hasClass(disabled)) {
            return;
        } else {
            button.addClass(disabled);
        }
    }
    if (typeof auto_extend === 'boolean') {
        params.disable_auto_extension = auto_extend;
    }
    if (this.wnd) {
        this.wnd.ajaxRequestPost('premium_features', 'extend_feature', params, {
            success: function (_wnd, data) {
                that.wnd.setContent2(data.html);
                that.enablePremiumFeatureInLayout(feature_type, data);
                that.rebindOnClickEvents({
                    remove_disabled_classes: true
                });
                Kontagent.trackEvent({
                    userId: Game.uid,
                    eventName: 'extend_' + feature_type,
                    subtype1: Game.market_id,
                    subtype2: Game.world_id,
                    subtype3: 'buy_pa_feature'
                }, function () {});
                var ev = GPEvents.global;
                ev.trigger(ev.spend_gold);
            },
            error: function (_wnd, data) {
                that.rebindOnClickEvents({
                    remove_disabled_classes: true
                });
            }
        });
    } else {
        Layout.ajaxPost('premium_features', 'extend_feature', params, true, function (data) {
            that.enablePremiumFeatureInLayout(feature_type, data);
        });
    }
};
WndHandlerPremium.prototype.rebindOnClickEvents = function (options) {
    var that = this,
        disabled = 'disabled',
        jQButtons = $('#pf_advisors li');
    jQButtons.unbind('click').click(function (e) {
        var target = $(e.target);
        if (target.hasClass('pf_extend_button')) {
            that.extendFeature(e, target.attr('href').split(/#/).reverse()[0]);
        } else {
            target = target.closest('div.premium_advisor_image');
            if (target.hasClass('premium_advisor_image')) {
                that.wnd.requestContentGet('premium_features', 'premium_overview', {}, function () {
                    that.premiumFeature(target.attr("name"));
                }, true);
            }
        }
    });
    if (options && options.remove_disabled_classes) {
        jQButtons.removeClass(disabled);
    }
};
WndHandlerPremium.prototype.setAutoExtension = function (feature_type) {
    this.wnd.requestContentPost('premium_features', 'set_auto_extension', {
        feature_type: feature_type
    });
};
WndHandlerPremium.prototype.buyGold = function () {
    this.wnd.clearMenu();
    this.wnd.requestContentGet('premium_features', 'buy_gold', {});
};
WndHandlerPremium.prototype.mentoring = function () {
    this.wnd.clearMenu();
    this.wnd.requestContentGet('mentoring', 'index', {});
};
WndHandlerPremium.prototype.sendInvitation = function () {
    var root = this.wnd.getJQElement();
    var email = root.find('[name=email]').val();
    this.wnd.requestContentPost('mentoring', 'invitation_send', {
        email: email
    });
};
WndHandlerPremium.prototype.premiumFeature = function (feature) {
    var root = this.wnd.getJQElement();
    var sl_cur = root.find('#premium_feature_sublist.curator');
    var sl_cap = root.find('#premium_feature_sublist.captain');
    if (feature == 'curator' || feature == 'notes' || (feature.indexOf('overview') != -1 && sl_cur.is(':visible'))) {
        sl_cur.slideDown();
        if (sl_cap.is(':visible')) {
            sl_cap.slideUp();
        }
    } else {
        if ((sl_cur.is(':visible') || sl_cap.is(':visible')) && (feature != 'attack_planer' && feature != 'farm_town_overview')) {
            sl_cur.slideUp();
            sl_cap.slideUp();
        }
        if (feature == 'captain') {
            sl_cap.slideDown();
        }
    }
    root.find('#premium_overview_info_area').children().fadeOut(500);
    this.wnd.ajaxRequestGet('premium_features', 'premium_feature', {
        feature_type: feature
    }, function (window, data) {
        root.find('#premium_overview_info_area').hide().html(data.html).fadeIn(500);
    });
};
WndHandlerPremium.prototype.enablePremiumFeatureInLayout = function (feature_type, data) {
    $('#' + feature_type).toggleClass('passive active');
    if (data.overviews_menu) {
        $('#city_overview_link').removeClass('inactive');
        $('#overviews_link_hover_menu').unbind().remove();
        $(data.overviews_menu).appendTo('body');
        Layout.bindPremiumStuff(feature_type);
    }
    if (feature_type == 'curator') {
        if (data.quickbar != '' && $('#toolbar').length <= 0) {
            Quickbar.refresh(data);
        }
        if (BuildingMain.isIndexWindowOpen() && BuildingMain.isBuildFromTownIndexEnabled()) {
            $('#building_tasks_index .main_tasks_hint').remove();
        }
        if (BuildingMain.isMainWindowOpen()) {
            $('#building_tasks_main .main_tasks_hint').remove();
        }
    }
    if (feature_type == 'captain') {
        $('#link_planer').show();
    }
};

function WndHandlerPlayerProfile(wndhandle) {
    this.wnd = wndhandle;
    this.elmnts = {};
}
WndHandlerPlayerProfile.inherits(WndHandlerDefault);
WndHandlerPlayerProfile.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        height: 600,
        width: 800,
        resizable: false,
        title: 'Untitled Window'
    };
    return ret;
};
WndHandlerPlayerProfile.prototype.onInit = function (title, UIopts) {
    var that = this;
    this.wnd.ajaxRequestGet('player', 'get_profile_html', {
        player_id: UIopts.player_id
    }, function (wnd, data) {
        that.wnd.setContent2(data.html);
        that.initAwards(data.awards);
    });
    return true;
};
WndHandlerPlayerProfile.prototype.onRcvData = function (data) {
    this.wnd.setContent2(data.html);
};
WndHandlerPlayerProfile.prototype.inviteIntoAlliance = function (player_name) {
    this.wnd.ajaxRequestPost('alliance', 'invite', {
        player_name: player_name
    }, function (_wnd, data) {});
};
WndHandlerPlayerProfile.prototype.initAwards = function (data) {
    var awards, award, i, j;
    if ($.isArray(data)) {
        return;
    }
    this.elmnts.awardlist = $('#awards');
    for (i in data) {
        awards = data[i];
        j = awards.length;
        while (j--) {
            award = awards[j];
            $('#' + award.award_id + '_' + award.level + '_' + award.created_at).mousePopup(new MousePopup('<b>' + award.name + '</b> (' + Timestamp.toDate(award.created_at).toShortString() + ')' + '<br />' + award.description));
        }
    }
    this.elmnts.awardlist.draggable({
        axis: 'x'
    });
};

function WndHandlerPlayerProfileEdit(wndhandle) {
    this.wnd = wndhandle;
    this.active_world = null;
    this.parent_elem = null;
    this.award_id = null;
    this.original_target = null;
    this.help = false;
}
WndHandlerPlayerProfileEdit.inherits(WndHandlerDefault);
WndHandlerPlayerProfileEdit.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        height: 510,
        width: 800,
        resizable: false,
        title: 'Untitled Window'
    };
    return ret;
};
WndHandlerPlayerProfileEdit.prototype.onInit = function (title, UIopts) {
    this.wnd.requestContentGet('player_profile', 'index', {});
    return true;
};
WndHandlerPlayerProfileEdit.prototype.onRcvData = function (data) {
    this.wnd.setContent(data.html);
};
WndHandlerPlayerProfileEdit.prototype.onSetContent = function (html) {
    var new_html = document.createElement('div');
    var wrapper;
    new_html.innerHTML = html;
    wrapper = $(new_html).find('div.bb_button_wrapper');
    this.bbcode = new BBCode(this.wnd, wrapper, '#edit_profile_text');
    return new_html;
};
WndHandlerPlayerProfileEdit.prototype.profilePreview = function () {
    var root = this.wnd.getJQElement();
    var text = root.find('#edit_profile_text').val();
    this.wnd.ajaxRequestPost('player_profile', 'profile_preview', {
        'profile_text': text
    }, function (window, data) {
        root.find('#profile_preview').html(data.profile_text);
    });
};
WndHandlerPlayerProfileEdit.prototype.profileEdit = function () {
    var root = this.wnd.getJQElement();
    var text = root.find('#edit_profile_text').val();
    this.wnd.ajaxRequestPost('player_profile', 'profile_edit', {
        'profile_text': text
    }, function (window, data) {
        root.find('#profile_preview').html(data.profile_text);
    });
};
WndHandlerPlayerProfileEdit.prototype.updateEmblem = function () {
    submit_form('emblem_form', 'player_profile', 'update');
};
WndHandlerPlayerProfileEdit.prototype.awardVisibilityInit = function (data) {
    var that = this;
    $('li.award_draggable').draggable({
        zIndex: 2000,
        appendTo: 'body',
        helper: function () {
            clone = $(this).clone();
            id = '_' + clone.attr('id');
            return clone.attr('id', id);
        },
        start: function (e, ui) {
            that.parent_elem = $(e.target).parent().attr('id');
            that.award_id = $(e.target).attr('id').replace("award_id_", "");
        },
        drag: function (e, ui) {
            if (that.original_target == null) {
                that.original_target = e.target;
                if (navigator.appName == "Microsoft Internet Explorer") {
                    $(that.original_target).attr('style', 'filter:alpha(opacity=50);');
                } else {
                    $(that.original_target).css('opacity', 0.5);
                }
            }
        },
        stop: function () {
            $(that.original_target).removeAttr('style');
            that.original_target = null;
            that.parent_elem = null;
        }
    });
    $('div.awards_all_drop_area').droppable({
        accept: function (d) {
            return (that.parent_elem === 'awards_visibility_ally_list' || that.parent_elem === 'awards_visibility_player_list');
        },
        activeClass: 'droppable-active',
        hoverClass: 'droppable-hover',
        drop: function (e, ui) {
            that.setVisibility(ui.draggable, 'all', that.parent_elem);
        }
    });
    $('div.awards_ally_drop_area').droppable({
        accept: function (d) {
            return (that.parent_elem === 'awards_visibility_all_list' || that.parent_elem === 'awards_visibility_player_list');
        },
        activeClass: 'droppable-active',
        hoverClass: 'droppable-hover',
        drop: function (e, ui) {
            that.setVisibility(ui.draggable, 'ally', that.parent_elem);
        }
    });
    $('div.awards_player_drop_area').droppable({
        accept: function (d) {
            return (that.parent_elem === 'awards_visibility_all_list' || that.parent_elem === 'awards_visibility_ally_list');
        },
        activeClass: 'droppable-active',
        hoverClass: 'droppable-hover',
        drop: function (e, ui) {
            that.setVisibility(ui.draggable, 'player', that.parent_elem);
        }
    });
    $.each(data, function (id, award) {
        $('#award_id_' + award.award_id).mousePopup(new MousePopup('<b>' + award.name + '</b>'));
    });
};
WndHandlerPlayerProfileEdit.prototype.setVisibility = function (award, visibility_new, visibility_old) {
    var data = {};
    data.award_id = award.attr('id').replace("award_id_", "");
    data.visibility = visibility_new;
    data.world_id = this.active_world;
    this.wnd.ajaxRequestPost('player_profile', 'set_award_visibility', data, function (return_data) {
        award.appendTo($('#awards_visibility_' + visibility_new + '_list'));
    });
};
WndHandlerPlayerProfileEdit.prototype.changeDefaultVisibility = function (visibility_new) {
    this.wnd.requestContentPost('player_profile', 'set_default_award_visibility', {
        default_visibility: $(visibility_new).val()
    });
};
WndHandlerPlayerProfileEdit.prototype.changeWorld = function (select) {
    var world_id = select.options[select.options.selectedIndex].value;
    this.wnd.requestContentPost('player_profile', 'award_visibility', {
        active_world: world_id
    });
};

function WndHandlerPlayerSettings(wndhandle) {
    this.wnd = wndhandle;
}
WndHandlerPlayerSettings.inherits(AbstractWndHandlerEmailValidation);
WndHandlerPlayerSettings.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        height: 550,
        width: 800,
        resizable: false,
        title: 'Untitled Window'
    };
    return ret;
};
WndHandlerPlayerSettings.prototype.onInit = function (title, UIopts, controller, action) {
    var args = UIopts || {};
    var index_controller = controller || 'player';
    var settings_action = action || 'index';
    var elm = $('#version');
    elm.appendTo(this.wnd.getJQElement()).show();
    this.wnd.requestContentGet(index_controller, settings_action, args);
    return true;
};
WndHandlerPlayerSettings.prototype.onClose = function () {
    var iframe = this.wnd.getJQElement().find('#player_settings iframe, .player_settings iframe');
    if (iframe.length > 0) {
        iframe.remove();
    }
    $('#version').appendTo('body').hide();
    return true;
};
WndHandlerPlayerSettings.prototype.onRcvData = function (data) {
    this.wnd.setContent2(data.html);
};
WndHandlerPlayerSettings.prototype.changePassword = function () {
    var root = this.wnd.getJQElement().find('#change_password_form');
    var params = {};
    params.old_password = root.find('[name=old_password]').val();
    if (!params.old_password) {
        HumanMessage.error('You haven\'t entered a valid password');
        return;
    }
    this.wnd.requestContentPost('player', 'do_change_password', params);
};
WndHandlerPlayerSettings.prototype.convertNHN = function () {
    var root = this.wnd.getJQElement().find('#convert_nhn_form');
    var params = {};
    params.new_password = root.find('[name=new_password]').val();
    params.new_password_confirm = root.find('[name=new_password_confirm]').val();
    params.email = root.find('[name=email]').val();
    params.email_confirm = root.find('[name=email_confirm]').val();
    if (!params.new_password || !params.new_password_confirm) {
        HumanMessage.error('You haven\'t entered a valid password');
        return;
    }
    if (params.new_password != params.new_password_confirm) {
        HumanMessage.error('The passwords don\'t match.');
        return;
    }
    if (!params.email || !params.email_confirm) {
        HumanMessage.error('You haven\'t entered a valid email address');
        return;
    }
    if (params.email != params.email_confirm) {
        HumanMessage.error('Your email addresses don\'t match.');
        return;
    }
    this.wnd.requestContentPost('player', 'do_convert_nhn_player', params);
};
WndHandlerPlayerSettings.prototype.saveSettings = function (reload_grepo) {
    var root = this.wnd.getJQElement();
    var setting_types = ['timezone', 'building_finished', 'notification_all_building_finished', 'new_report', 'new_message', 'use_localstore', 'windowmgr_max_concurrent', 'build_from_town_index_enabled', 'gfx_level', 'auto_open_town_index_enabled', 'map_arrow_show_always', 'notification_building_finished', 'notification_block_invitation', 'show_confirmation_popups'];
    var settings = {};
    $.each(setting_types, function (i, setting) {
        var input = root.find('#player_settings [name=' + setting + ']');
        if (input.length > 0) {
            var value = input.is('[type="checkbox"]') ? input.is(':checked') : input.val();
            settings[setting] = value;
        }
    });
    this.wnd.ajaxRequestPost('player', 'save_reminders', settings, function (window, data) {
        if (reload_grepo) {
            location.reload();
        }
    });
};
WndHandlerPlayerSettings.prototype.changeMapDirectionArrow = function () {
    var that = this;
    var map_direction_arrow = this.wnd.getJQElement().find('#map_direction_arrow').val();
    this.wnd.ajaxRequestPost('player', 'edit_map_direction_arrow', {
        map_direction_arrow: map_direction_arrow
    }, function (window, data) {
        that.saveSettings(true);
    });
};
WndHandlerPlayerSettings.prototype.insertSharedConnection = function () {
    var root = this.wnd.getJQElement();
    var params = {};
    params.name = root.find('#insert_shared_connection_player').val();
    if (!params.name) {
        HumanMessage.error('The player was not found');
        return;
    }
    this.wnd.ajaxRequestPost('player', 'insert_shared_connection', params, function (window, data) {
        root.find('#shared_connection_table').append(data.insert_html);
        root.find('#shared_connections').show();
        root.find('#insert_shared_connection_player').val('');
    });
};
WndHandlerPlayerSettings.prototype.deleteSharedConnection = function (shared_player_id) {
    var root = this.wnd.getJQElement();
    this.wnd.ajaxRequestPost('player', 'delete_shared_connection', {
        shared_player_id: shared_player_id
    }, function (window, data) {
        root.find('#shared_connection_' + data.shared_player_id).remove();
        if (root.find('#shared_connection_table tr').length == 0) {
            root.find('#shared_connections').hide();
        }
    });
};
WndHandlerPlayerSettings.prototype.startVacation = function () {
    var wnd = this.wnd;
    Layout.showConfirmDialog('Activate vacation mode?', 'The vacation mode can be stopped at the earliest 24 hours after starting it, by logging in. ', function () {
        Layout.ajaxPost('player', 'start_vacation', {}, true, function (data) {
            wnd.setContent2(data.html);
        });
    });
};
WndHandlerPlayerSettings.prototype.buyVacationDays = function (buy_vacation_days_gold, button) {
    var days = parseInt($('#vacation_order_input').val(), 10),
        text, disabled = 'inactive',
        confirm, cancel, that = this;
    button = $(button);
    if (!days || button.hasClass(disabled)) {
        return;
    }
    text = ngettext('Do you want to buy %1$d vacation day for %2$d gold?', new Array('Do you want to buy %1$d vacation day for %2$d gold?', 'Do you want to buy %1$d vacation days for %2$d gold?'), days);
    text = text.replace('%1$d', days).replace('%2$d', (days * buy_vacation_days_gold));
    confirm = function () {
        var ev = GPEvents.global;
        Layout.ajaxPost('player', 'buy_vacation_days', {
            days: days
        }, false, function (data) {
            that.wnd.setContent2(data.html);
        });
        ev.trigger(ev.spend_gold);
    };
    button.addClass(disabled);
    if (Layout.show_confirmation_popup) {
        cancel = function () {
            button.removeClass(disabled);
        };
        Layout.showConfirmDialog('Buy vacation days?', text, confirm, null, cancel, null, null, 'Do not show this window again');
    } else {
        confirm();
    }
};
WndHandlerPlayerSettings.prototype.deleteAccount = function () {
    var password = this.wnd.getJQElement().find('#delete_request_password').val();
    if (password == '') {
        HumanMessage.error('You have to enter your password to authorize the deletion of your account.');
        return;
    }
    Layout.showConfirmDialog('Delete?', 'Are you sure that you want to authorize the deletion of your account?', function () {
        Layout.ajaxPost('player', 'do_delete_account', {
            password: password
        }, function () {});
    });
};
WndHandlerPlayerSettings.prototype.supportPopup = function (width, height) {
    var support_window = window.open('game/player?action=support', 'popup', 'width=' + width + ',height=' + height + ',resizable=yes,scrollbars=yes');
    support_window.focus();
};
WndHandlerPlayerSettings.prototype.restartDialog = function () {
    var root = this.wnd.getJQElement().find('#player_settings div.game_inner_box');
    var confirmed = root.find('input:checkbox').is(':checked');
    var password = root.find('input:password').val();
    if (!confirmed) {
        HumanMessage.error('You must confirm the restart first');
        return;
    }
    Layout.showConfirmDialog('Restart', 'Are you sure you want to restart the game?', function () {
        Layout.ajaxPost('player', 'do_restart', {
            confirmed: confirmed,
            password: password
        }, true, function (data) {
            if (data.success) {
                window.location.href = url('login', null, {
                    session_id: $.cookie('sid')
                });
            }
        });
    });
    return;
};
WndHandlerPlayerSettings.prototype.setPasswordForFbUser = function () {
    var params = {};
    params.new_password = $('#change_password_form [name=new_password]').val();
    params.new_password_confirm = $('#change_password_form [name=new_password_confirm]').val();
    if (!params.new_password || !params.new_password_confirm) {
        HumanMessage.error('You haven\'t entered a valid password');
        return;
    }
    if (params.new_password != params.new_password_confirm) {
        HumanMessage.error('The passwords don\'t match.');
        return;
    }
    this.wnd.requestContentPost('facebook_connect', 'set_password_for_fb_user', params);
};
WndHandlerPlayerSettings.prototype.facebookRemoveUid = function () {
    var that = this;
    if (confirm('Disconnect account?')) {
        that.wnd.requestContentPost('facebook_connect', 'revoke_facebook_connection', {});
    }
};

function WndHandlerColorTable(wndhandle) {
    this.wnd = wndhandle;
}
WndHandlerColorTable.inherits(WndHandlerDefault);
WndHandlerColorTable.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        height: 380,
        width: 490
    };
    return ret;
};
WndHandlerColorTable.prototype.onInit = function (title, UIopts) {
    this.wnd.requestContentGet('map_data', 'get_custom_colors', {});
    return true;
};
WndHandlerColorTable.prototype.onRcvData = function (data) {
    if (data.list_html) {
        this.wnd.setContent2(data.list_html)
    }
};

WndHandlerConquest = function (wndhandle) {
    this.wnd = wndhandle;
    this.currenttownId = 0;
};
WndHandlerConquest.inherits(WndHandlerDefault);
WndHandlerConquest.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        height: 330,
        width: 480,
        resizable: false,
        title: 'Your city is currently being conquered:'
    };
    return ret;
};
WndHandlerConquest.prototype.onInit = function (title, UIopts, town_id) {
    this.wnd.sendMessage('setTown', town_id);
    return true;
};
WndHandlerConquest.prototype.onClose = function () {
    if (Game.townId == this.currenttownId) {
        return false;
    }
    return true;
};
WndHandlerConquest.prototype.onRcvData = function (data) {
    var that = this;
    this.wnd.setContent2(data.html);
    if (data.json.movements) {
        var movs = data.json.movements,
            i = movs.length,
            mov;
        while (i--) {
            mov = movs[i];
            $('#command_countdown_' + mov[0]).countdown(mov[1]);
            $('#command_countdown_' + mov[0]).bind('finish', function () {
                MenuBubbleMovement.menu_bubble.forceUpdate();
                that.wnd.requestContentGet('conquest_info', 'getinfo', {
                    'town_id': that.currenttownId
                });
            });
        }
    }
}
WndHandlerConquest.prototype.onMessage = function (msgID, p1) {
    switch (msgID) {
    case 'setTown':
        if (p1 == this.currenttownId) return;
        this.currenttownId = p1;
        this.wnd.requestContentGet('conquest_info', 'getinfo', {
            'town_id': p1
        });
        break;
    }
}

WndHandlerAtkCommand = function (window) {
    this.wnd = window;
    this.command_id = -1;
};
WndHandlerAtkCommand.inherits(WndHandlerDefault);
WndHandlerAtkCommand.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        height: 390,
        width: 468,
        resizable: false,
        title: 'Untitled Window'
    };
    return ret;
};
WndHandlerAtkCommand.prototype.onInit = function (title, UIopts, commandID) {
    this.wnd.requestContentGet('command_info', 'info', {
        'command_id': commandID
    });
    return true;
};
WndHandlerAtkCommand.prototype.onRcvData = function (data) {
    var html, tmpl, powers, descr, that = this;
    this.command_id = data.command_id;
    if (data.html) {
        this.wnd.setContent2(data.html);
    } else {
        tmpl = data.tmpl.split('|||');
        powers = tmpl.shift();
        descr = tmpl.pop();
        GameData.add({
            'PowerDescriptionTemplate': descr
        });
        html = tmpl2(powers, data.json);
        this.wnd.setContent(html);
    }
    try {
        $('#eta-command-' + this.command_id).countdownAddElement($('#arrival_at_countdown'));
    } catch (e) {
        $('#arrival_' + this.command_id).countdown(data.arrival_at).bind('finish', function () {
            that.wnd.close();
        });
    }
    $('#cancel_command_' + this.command_id).click(function () {
        that.cancel();
    });
};
WndHandlerAtkCommand.prototype.showPowerDescription = function (power) {
    WndHandlerTown.prototype.showPowerDescription.call(this, power);
};
WndHandlerAtkCommand.prototype.castPower = function (power) {
    this.wnd.ajaxRequestPost('command_info', 'cast', {
        'command_id': this.command_id,
        'power': power
    }, function () {});
};
WndHandlerAtkCommand.prototype.cancel = function () {
    var that = this;
    this.wnd.ajaxRequestPost('command_info', 'cancel_command', {
        'id': this.command_id
    }, function (wnd, data) {
        if (data.success) {
            wnd.requestContentGet('command_info', 'info', {
                'command_id': that.command_id
            });
        }
    });
};

WndHandlerConqueror = function (wndhandle) {
    this.wnd = wndhandle;
    this.command_id = -1;
    this.other_town_id = -1;
};
WndHandlerConqueror.inherits(WndHandlerDefault);
WndHandlerConqueror.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        height: 330,
        width: 480,
        resizable: false,
        title: 'Conquest information'
    };
    return ret;
};
WndHandlerConqueror.prototype.onInit = function (title, UIopts, command_id, other_town_id) {
    this.command_id = command_id;
    this.other_town_id = other_town_id;
    this.wnd.requestContentGet('command_info', 'conquest_info', {
        'command_id': this.command_id
    });
    return true;
};
WndHandlerConqueror.prototype.onClose = function () {
    return true;
};
WndHandlerConqueror.prototype.onRcvData = function (data) {
    this.wnd.setContent2(data.html);
    if (data.json.movements) {
        var movs = data.json.movements,
            i = movs.length,
            mov;
        while (i--) {
            mov = movs[i];
            $('#command_countdown_' + mov[0]).countdown(mov[1]);
        }
    }
    this.wnd.getJQElement().find('span.eta').countdown(data.json.finished);
}
WndHandlerConqueror.prototype.showTroops = function () {
    this.wnd.getJQElement().find('span.countdown').countdown();
    this.wnd.requestContentGet('command_info', 'conquest_movements', {
        'town': this.other_town_id
    });
}

function WndHandlerQuest(wndhandle) {
    this.wnd = wndhandle;
}
WndHandlerQuest.inherits(WndHandlerDefault);
WndHandlerQuest.prototype.closable = false;
WndHandlerQuest.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        maxHeight: 380,
        maxWidth: 500,
        height: 270,
        width: 400,
        resizable: false,
        title: 'Tutorial - Step (x/x)'
    };
    return ret;
};
WndHandlerQuest.prototype.onInit = function (title, UIopts) {
    return true;
};
WndHandlerQuest.prototype.onClose = function () {
    if (this.closable == false) {
        Tutorial.closeQuestDialog();
    } else {
        Tutorial.onClose();
    }
    return this.closable;
};
WndHandlerQuest.prototype.onRcvData = function (data) {
    this.wnd.setContent2(data.html);
};
WndHandlerQuest.prototype.getPage = function () {
    return null;
};
WndHandlerQuest.prototype.onMessage = function () {
    switch (arguments[0]) {
    case 'loadoverview':
        this.wnd.requestContentGet('quest', 'overview', {});
        this.closable = true;
        break;
    }
    return null;
};

function WndHandlerPhoenicianSalesman(wndhandle) {
    this.wnd = wndhandle;
}
WndHandlerPhoenicianSalesman.inherits(WndHandlerDefault);
WndHandlerPhoenicianSalesman.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        maxHeight: 560,
        maxWidth: 790,
        height: 560,
        width: 790,
        resizable: false,
        yOverflowHidden: true,
        title: 'Phoenician merchant',
        help: true
    };
    return ret;
};
WndHandlerPhoenicianSalesman.prototype.onInit = function (title, UIopts) {
    this.wnd.sendMessage('loadPhoenicianSalesmanView', 'phoenician_salesman', {}, true);
    return true;
};
WndHandlerPhoenicianSalesman.prototype.onClose = function () {
    Layout.phoenicianSalesman.close(true);
    return true;
};
WndHandlerPhoenicianSalesman.prototype.onRcvData = function (data) {
    this.wnd.setContent2(data.html);
};
WndHandlerPhoenicianSalesman.prototype.onMessage = function (message, controller) {
    switch (message) {
    case 'loadPhoenicianSalesmanView':
        this.wnd.requestContentGet(controller, 'index', {
            town_id: 0
        });
        break;
    }
    return null;
};
WndHandlerPhoenicianSalesman.prototype.showHelp = function () {
    var title = 'Phoenician merchant' + '-' + 'Information';
    var text = 'The Phoenician merchant offers you: units in exchange for silver coins, and resources in exchange for other resources.' + '<br />' + '<br />' + 'While the merchant always offers the same amount of unit types, the amount of offered resources depends on the level of your harbor.' + '<br />' + '<br />' + s('You can also improve the exchange ratio from %1 to %2 with a merchant\'s shop.', '1 : 0.67', '1 : 0.5');
    Layout.dialogWindow.open(text, title, 400, 230);
};

function WndHandlerBuilding(wndhandle) {
    var that = this;
    this.wnd = wndhandle;
    this.currentBuilding = '';
    this.onclose_hook = null;
    this.current_tab = null;
    this.setToTop = function () {
        if (!Layout.wnd.getOpenFirst(Layout.wnd.TYPE_QUEST)) {
            this.wnd.toTop();
        }
    };
    this.registerEventListeners = function () {
        var that = this,
            g = GPEvents.global,
            name = '.WndHandlerBuilding';
        switch (this.currentBuilding) {
        case 'barracks':
            g.bind(g.town.units_changed + name, UnitOrder.handleEvents);
            g.bind(g.town.unit_order_changed + name, function () {
                that.reloadBuildingWithUnitOrders(that.currentBuilding);
            });
            break;
        case 'docks':
            g.bind(g.town.units_changed + name, UnitOrder.handleEvents);
            g.bind(g.town.unit_order_changed + name, function () {
                that.reloadBuildingWithUnitOrders(that.currentBuilding);
            });
            break;
        case 'main':
            BuildingMain.tear_down_menu = false;
            this.wnd.getJQElement().click(that.clickHandlerMain);
            this.wnd.getJQElement().find('div.advisor_hint').find('a.button').mousePopup(new MousePopup(PopupFactory.texts.curator_hint));
            g.bind(g.town.resources_update + name, BuildingMain.handleEvents);
            g.bind(g.town.building_order_changed + name, BuildOrders.handleEvents);
            break;
        case 'academy':
            BuildingAcademy.revert_menu = false;
            g.bind(g.town.resources_update + name, BuildingAcademy.handleEvents);
            break;
        case 'place':
            g.bind(g.town.units_changed + name, function () {
                BuildingPlace.handleEvents(that.current_tab);
            });
            break;
        default:
            break;
        }
        return this;
    };
    this.unregisterEventListeners = function () {
        GPEvents.global.unbind('.WndHandlerBuilding');
        if (BuildingFarm.timer) {
            window.clearInterval(BuildingFarm.timer);
            BuildingFarm.timer = null;
        }
        return this;
    };
    this.refresh = function () {
        var action, building;
        this.unregisterEventListeners();
        action = this.current_tab || 'index';
        building = GameData.buildings[this.currentBuilding];
        this.wnd.clearMenuNow();
        this.wnd.requestContentGet(building.controller, action, {
            town_id: Game.townId
        });
        this.setToTop();
        this.wnd.setTitle('Building view' + ' - ' + building.name + ' (' + Game.townName + ')');
        this.registerEventListeners();
    };
}
WndHandlerBuilding.inherits(WndHandlerBuild);
WndHandlerBuilding.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        maxHeight: 900,
        maxWidth: 1200,
        height: 570,
        width: 790,
        resizable: true,
        minimizable: true,
        yOverflowHidden: true
    };
    return ret;
};
WndHandlerBuilding.prototype.onInit = function (title, UIopts, building, action, params) {
    if (!building) {
        throw 'No building type specified.';
    }
    action = action || 'index';
    var controller = GameData.buildings[building].controller;
    this.wnd.requestContentGet(controller, action, params || {});
    return true;
};
WndHandlerBuilding.prototype.onClose = function () {
    if (this.onclose_hook) {
        if (!this.onclose_hook()) {
            HumanMessage.error('This window can\'t be closed. Finish the tutorial first.');
            return false;
        }
    }
    this.unregisterEventListeners();
    return true;
};
WndHandlerBuilding.prototype.onFocus = function () {
    var wnd;
    if ((wnd = GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_QUEST))) {
        wnd.toTop();
    }
};
WndHandlerBuilding.prototype.onRcvData = function (data, controller, action) {
    var newBuilding = controller.replace(/building_/, '');
    if (newBuilding !== this.currentBuilding) {
        this.setToTop();
        this.wnd.setTitle('Building view' + ' - ' + GameData.buildings[newBuilding].name + ' (' + Game.townName + ')');
        this.currentBuilding = newBuilding;
    }
    if (action && (controller == 'building_main' || controller == 'building_place')) {
        this.current_tab = action;
    }
    if (data.building_orders_tmpl) {
        GameData.add({
            'BuildingOrdersTemplate': data.building_orders_tmpl,
            'BuildingOrdersTemplateNew': data.building_orders_tmpl_new,
            'abctest_time_reduction': data.abctest_time_reduction
        });
    }
    if (this.currentBuilding == 'place') {
        BuildingPlace.wnd_handle = this.wnd;
        if (data.building_place_tmpl) {
            GameData.add({
                'BuildingPlaceTemplate': data.building_place_tmpl
            });
            BuildingPlace.index_data = data.data;
            BuildingPlace.renderIndex(this.wnd);
            return;
        }
    }
    this.wnd.setContent2(data.html);
    this.registerEventListeners();
    if (this.currentBuilding === 'academy') {
        this.unregisterEventListeners().registerEventListeners();
    }
};
WndHandlerBuilding.prototype.onMessage = function (type, building_type, is_building, action, parameters) {
    type = type || '';
    building_type = building_type || '';
    is_building = is_building || false;
    action = action || 'index';
    parameters = parameters || {};
    var building;
    switch (type) {
    case 'setOnCloseHook':
        this.onclose_hook = building_type;
        break;
    case 'unsetOnCloseHook':
        this.onclose_hook = null;
        break;
    }
    return null;
};
WndHandlerBuilding.prototype.submitForm = function (form_id, action) {
    var params = {};
    $('#' + form_id + ' input').each(function () {
        params[this.name] = this.value;
    });
    var building = GameData.buildings[this.currentBuilding];
    this.wnd.requestContentPost(building.controller, action, params);
};
WndHandlerBuilding.prototype.reloadBuildingWithUnitOrders = function (building) {
    var wnd = GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_MARKET) || GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_BUILDING);
    if (wnd) {
        building = GameData.buildings[building];
        wnd.requestContentGet(building.controller, 'index', {});
    }
};

function WndHandlerRanking(wndhandle) {
    this.wnd = wndhandle;
}
WndHandlerRanking.inherits(WndHandlerDefault);
WndHandlerRanking.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        maxHeight: 500,
        maxWidth: 820,
        height: 500,
        width: 820,
        resizable: true,
        title: 'Ranking - XX'
    };
    return ret;
};
WndHandlerRanking.prototype.onInit = function (title, UIopts) {
    this.wnd.requestContentGet('ranking', 'index', {
        'rank': arguments[2],
        'town_id': arguments[3]
    });
    return true;
};
WndHandlerRanking.prototype.onClose = function () {
    return true;
};
WndHandlerRanking.prototype.onRcvData = function (data) {
    this.wnd.setContent2(data.html);
};
WndHandlerRanking.prototype.getPage = function () {
    return null;
};
WndHandlerRanking.prototype.search = function (action, use, rank_elm_name, search_elm_name, args) {
    if (typeof args == 'undefined') {
        args = {};
    }
    var search_term = $('#' + search_elm_name).val();
    var rank = $('#' + rank_elm_name).val();
    switch (use) {
    case 'rank':
        args.rank = rank;
        break;
    case 'search':
        args.search_term = search_term;
        break;
    case 'both':
        args.rank = rank;
        args.search_term = search_term;
        break;
    }
    this.wnd.requestContentGet('ranking', action, args);
    return true;
};

function WndHandlerAlliance(wndhandle) {
    this.wnd = wndhandle;
    this.updateMap = false;
}
WndHandlerAlliance.inherits(WndHandlerDefault);
WndHandlerAlliance.prototype.getDefaultWindowOptions = function () {
    return {
        position: ['center', 'center'],
        maxHeight: 900,
        maxWidth: 1200,
        height: 500,
        width: 800,
        resizable: true,
        minimizable: true,
        title: 'Alliance'
    };
};
WndHandlerAlliance.prototype.onInit = function (title, UIopts, town_id) {
    this.wnd.requestContentGet('alliance', 'index', {
        'town_id': town_id
    });
    return true;
};
WndHandlerAlliance.prototype.onClose = function () {
    return true;
};
WndHandlerAlliance.prototype.onRcvData = function (data, controller, action) {
    var html = document.createElement('div');
    var wrapper;
    html.innerHTML = data.html;
    wrapper = $(html).find('div.bb_button_wrapper');
    switch (action) {
    case "index":
        this.bbcode = new BBCode(this.wnd, wrapper, '#ally_announce_textarea');
        break;
    case "properties":
        this.bbcode = new BBCode(this.wnd, wrapper, '#ally_profile_textarea');
        break;
    }
    this.wnd.setContent2(html);
    if (this.updateMap) {
        this.updateMap = false;
        WMap.pollForMapChunksUpdate();
    }
};
WndHandlerAlliance.prototype.getPage = function () {
    return null;
};
WndHandlerAlliance.prototype.inviteIntoAlliance = function () {
    var player_name = $('#' + this.wnd.getName() + ' #invitation_form input[name=name]').val();
    this.wnd.requestContentPost('alliance', 'invite', {
        player_name: player_name
    });
};
WndHandlerAlliance.prototype.createAlliance = function (name) {
    this.wnd.clearMenu();
    this.updateMap = true;
    this.wnd.requestContentPost('alliance', 'create', {
        name: name
    });
    Layout.showAllianceForumLink();
};
WndHandlerAlliance.prototype.joinAlliance = function (alliance_id) {
    this.wnd.clearMenu();
    this.updateMap = true;
    this.wnd.requestContentPost('alliance', 'join', {
        alliance_id: alliance_id
    });
    $('#links ul').append('<li><a href="#chat" id="chat_link">' + 'Chat' + '</a></li>');
    Layout.showAllianceForumLink();
};
WndHandlerAlliance.prototype.leaveAlliance = function () {
    var that = this;
    this.updateMap = true;
    this.wnd.ajaxRequestPost('alliance', 'leave', {}, function (_wnd, data) {
        that.wnd.setContent2(data.html);
        that.wnd.clearMenuNow();
        $('#chat_link').remove();
        var chat_w = Layout.wnd.getOpenFirst(Layout.wnd.TYPE_CHAT);
        if (chat_w) {
            chat_w.close();
        }
        var forum_w = Layout.wnd.getOpenFirst(Layout.wnd.TYPE_ALLIANCE_FORUM);
        if (forum_w) {
            forum_w.close();
        }
        Layout.hideAllianceForumLink();
    });
};
WndHandlerAlliance.prototype.rejectAllianceInvitation = function (invitation_id) {
    this.wnd.requestContentPost('alliance', 'reject_invitation', {
        id: invitation_id
    });
};
WndHandlerAlliance.prototype.membersShow = function () {
    this.wnd.requestContentGet('alliance', 'members_show', {
        edit: true
    });
};
WndHandlerAlliance.prototype.members = function () {
    this.wnd.requestContentPost('alliance', 'members', {
        rights: Alliance.fetchMemberRights(),
        title: Alliance.fetchMemberTitle()
    });
};
WndHandlerAlliance.prototype.pactInvite = function () {
    this.wnd.requestContentPost('alliance', 'pact_invite', {
        name: $("#ally_pact_invitation_form_name").attr('value')
    });
};
WndHandlerAlliance.prototype.updateEmblem = function () {
    this.wnd.requestContentPost('alliance', 'updateEmblem', {});
};
WndHandlerAlliance.prototype.deleteAlliance = function () {
    var wnd = this.wnd;
    this.wnd.clearMenu();
    Layout.showConfirmDialog('Dissolve alliance', 'Are you sure that you want to dissolve the alliance?', function () {
        Layout.ajaxPost('alliance', 'delete', {}, true, function (data) {
            wnd.setContent2(data.html);
            WMap.pollForMapChunksUpdate();
            Layout.hideAllianceForumLink();
        });
    });
};

function WndHandlerDirectionXSelling(wndhandle) {
    this.wnd = wndhandle;
    this.updateMap = false;
    window.Layout = {};
}
WndHandlerDirectionXSelling.inherits(WndHandlerDefault);
WndHandlerDirectionXSelling.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        maxHeight: 900,
        maxWidth: 1200,
        height: 250,
        width: 480,
        resizable: false,
        minimizable: false,
        title: 'Welcome to Grepolis!'
    };
    return ret;
};
WndHandlerDirectionXSelling.prototype.onInit = function (title, UIopts, town_id) {
    var that = this;
    this.bg = $('<div id="blockbox_bg"></div>');
    this.bg.click(function () {
        that.wnd.close();
    }).appendTo('body').show();
    this.wnd.setContent($('#welcome').html());
    this.wnd.getJQElement().find('.button').click(function () {
        that.wnd.close();
    });
    return true;
};
WndHandlerDirectionXSelling.prototype.onClose = function () {
    this.bg.remove();
    return true;
};

function WndHandlerFarmTownOverviews(wndhandle) {
    this.wnd = wndhandle;
    this.island_x = 0;
    this.island_y = 0;
    this.current_town_id = 0;
    this.booty_researched = 0;
    this.trade_office = 0;
    this.loads_data = null;
    this.farm_towns = null;
    this.selected_farm_towns = [];
    this.selected_time_option = 300;
    this.max_resources = null;
    this.max_satisfaction_reduce = null;
    this.pillage_menu = false;
}
WndHandlerFarmTownOverviews.inherits(WndHandlerDefault);
WndHandlerFarmTownOverviews.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        height: 525,
        width: 768,
        resizable: false,
        minimizable: false,
        title: 'Untitled Window'
    };
    return ret;
};
WndHandlerFarmTownOverviews.prototype.onInit = function (title, UIopts) {
    this.wnd.requestContentGet('farm_town_overviews', 'index', {});
    return true;
};
WndHandlerFarmTownOverviews.prototype.onRcvData = function (data) {
    $(document).stopTime('WndHandlerFarmTownOverviews::initializeResourcesProductionCounter::timer');
    this.initializeResourcesCounter(data.towns);
    this.bindMenuEvents();
    this.invalideData(false);
    this.wnd.setContent2(data.html);
    this.bindTownClick();
};
WndHandlerFarmTownOverviews.prototype.onClose = function () {
    $(document).stopTime('WndHandlerFarmTownOverviews::initializeResourcesProductionCounter::timer');
    return true;
};
WndHandlerFarmTownOverviews.prototype.getFarmTowns = function (params) {
    var that = this;
    this.invalideData(false);
    this.wnd.ajaxRequestGet('farm_town_overviews', 'get_farm_towns_for_town', params, function (_wnd, data) {
        var farm_town_wrapper = $('#farm_town_wrapper');
        that.loads_data = data.loads_data;
        that.farm_towns = data.farm_town_list;
        that.selected_time_option = 300;
        that.selectAllFarms(false);
        farm_town_wrapper.html(data.html);
        that.bindFarmTownClick(farm_town_wrapper);
        that.bindOptionsClick();
        that.changeMaxResources();
        $('#fto_claim_button').find('span.middle').html((that.pillage_menu ? 'looting' : 'demand'));
        $('#fto_farm_claim_new_res').mousePopup(new MousePopup('<h4>' + 'Resources in the current city' + '</h4><p>' + 'Amount of resources that are available after making a demand or looting.' + '</p>'));
        $('#max_satisfaction_reduce').parent().mousePopup(new MousePopup('<h4>' + 'Mood decrease' + '</h4><p>' + 'Low village mood can lead to a loss of control over the farming village.' + '</p>'));
        $('#max_claim_resources').parent().mousePopup(new MousePopup('<h4>' + 'Maximum possible resources' + '</h4><p>' + 'Farming villages whose loads are not ready will not be considered.' + '</p>'));
    });
};
WndHandlerFarmTownOverviews.prototype.changeMaxResources = function () {
    this.max_resources = 0;
    this.max_satisfaction_reduce = 0;
    var i = this.selected_farm_towns.length;
    var modification = this.pillage_menu ? 2 : 1;
    while (i--) {
        var k = this.farm_towns.length;
        while (k--) {
            if (this.farm_towns[k].id == this.selected_farm_towns[i]) {
                this.max_resources += parseInt(this.loads_data[this.selected_time_option].resources[this.farm_towns[k].stage - 1] * modification, 10);
                if (this.pillage_menu) {
                    this.max_satisfaction_reduce = this.loads_data[this.selected_time_option].mood;
                }
            }
        }
    }
    var resources = ['wood', 'stone', 'iron'];
    var town = ITowns.getTown(this.current_town_id);
    var storage = town.storage();
    var res = town.resources();
    var l = resources.length;
    while (l--) {
        var id = resources[l];
        var elm = $('#fto_' + id + '_exceeded span.count');
        if ((res[id] + this.max_resources) >= storage) {
            elm.html(storage)[0].className = 'count small town_storage_full';
        } else {
            elm.html(res[id] + this.max_resources)[0].className = 'count small';
        }
    }
    $('#max_claim_resources').html('+' + this.max_resources);
    $('#max_satisfaction_reduce').html(this.max_satisfaction_reduce > 0 ? ('-' + this.max_satisfaction_reduce) : this.max_satisfaction_reduce);
};
WndHandlerFarmTownOverviews.prototype.claimLoads = function () {
    var i = this.selected_farm_towns.length;
    var farm_town_ids = [];
    var that = this;
    if (this.selected_farm_towns.length <= 0) {
        HumanMessage.error('You haven\'t selected any farming villages.');
        return;
    }
    if (this.selected_time_option == null) {
        HumanMessage.error('You haven\'t selected any time options.');
        return;
    }
    while (i--) {
        farm_town_ids.push(~~this.selected_farm_towns[i]);
    }
    this.wnd.ajaxRequestPost('farm_town_overviews', 'claim_loads', {
        farm_town_ids: farm_town_ids,
        time_option: this.selected_time_option,
        claim_factor: (this.pillage_menu ? 'double' : 'normal'),
        current_town_id: this.current_town_id
    }, function (_wnd, data) {
        var j, k;
        for (j in data.resources) {
            var elm = $('#fto_town_list li.town' + that.current_town_id + ' span.fto_resource_count.' + j + ' span.count');
            elm.html(data.resources[j]);
            elm[0].className = (data.storage > data.resources[j]) ? 'small count' : 'small count town_storage_full';
        }
        for (k in data.handled_farms) {
            if (data.handled_farms[k].relation_status == 2) {
                WMap.updateStatusInChunkTowns(k, data.handled_farms[k].satisfaction, Timestamp.now() + that.selected_time_option, Timestamp.now(), data.handled_farms[k].lootable_human, 2);
                WMap.pollForMapChunksUpdate();
                WMap.changeFarmTownFlag(k, 2);
            } else {
                WMap.updateStatusInChunkTowns(k, data.handled_farms[k].satisfaction, Timestamp.now() + that.selected_time_option, Timestamp.now(), data.handled_farms[k].lootable_human);
            }
            WMap.hideLoadIcon(k);
        }
        that.getFarmTowns({
            island_x: that.island_x,
            island_y: that.island_y,
            booty_researched: that.booty_researched,
            trade_office: that.trade_office
        });
    });
};
WndHandlerFarmTownOverviews.prototype.invalideData = function (change_button) {
    this.selected_farm_towns = [];
    this.max_resources = null;
    this.max_satisfaction_reduce = null;
    var pillage = this.pillage_menu;
    if (change_button) {
        $('#fto_claim_button').find('span.middle').html((pillage ? 'looting' : 'demand'));
    }
};
WndHandlerFarmTownOverviews.prototype.selectAllFarms = function (add_highlighting) {
    if (add_highlighting) {
        var wrapper = $('#farm_town_list');
        wrapper.find('a.checkbox').parent().removeClass('active');
        wrapper.find('a.checkbox').removeClass('checked');
    }
    var k = this.farm_towns.length;
    while (k--) {
        if (this.farm_towns[k].rel == 1 && this.farm_towns[k].loot <= Timestamp.now()) {
            this.selected_farm_towns.push(this.farm_towns[k].id);
            if (add_highlighting) {
                var farm_check = wrapper.find('a.checkbox.farm_town_' + this.farm_towns[k].id);
                farm_check.parent().addClass('active');
                farm_check.addClass('checked');
            }
        }
    }
};
WndHandlerFarmTownOverviews.prototype.bindFarmTownClick = function (farm_town_wrapper) {
    var that = this;
    farm_town_wrapper.find('li.owned').unbind().click(function (e) {
        if (e.target.className && e.target.className.match('gp_town_link')) {
            return;
        }
        var elm = $(this);
        var checkbox = elm.find('a.checkbox');
        var farm_town_id = parseInt(checkbox[0].className.match(/\d+/), 10);
        if (checkbox.hasClass('checked')) {
            elm.removeClass('active');
            checkbox.removeClass('checked');
            var i = that.selected_farm_towns.indexOf(farm_town_id);
            that.selected_farm_towns.splice(i, 1);
        } else {
            elm.addClass('active');
            checkbox.addClass('checked');
            that.selected_farm_towns.push(farm_town_id);
        }
        that.changeMaxResources();
    });
};
WndHandlerFarmTownOverviews.prototype.bindOptionsClick = function () {
    var that = this;
    $('#farm_town_options').show().find('div.fto_time_checkbox').unbind().click(function () {
        var elm = $(this);
        var parent = elm.parent();
        that.selected_time_option = ~~elm[0].className.match(/\d+/);
        parent.find('a').removeClass('checked');
        parent.find('div.fto_time_checkbox').removeClass('active');
        elm.toggleClass('active');
        elm.find('a').toggleClass('checked');
        that.changeMaxResources();
    });
    $('#fto_claim_button').unbind().click(function () {
        that.claimLoads();
    });
};
WndHandlerFarmTownOverviews.prototype.bindMenuEvents = function () {
    var that = this;
    $('#fto_claim').click(function () {
        that.pillage_menu = false;
        that.invalideData(true);
        that.selectAllFarms(true);
        that.changeMaxResources();
    });
    $('#fto_pillage').click(function () {
        that.pillageClick();
    });
};
WndHandlerFarmTownOverviews.prototype.bindTownClick = function () {
    var that = this,
        params = {};
    $('#fto_town_list').unbind().click(function (e) {
        var target = $(e.target);
        var classes = null;
        if (!target.hasClass('fto_town')) {
            target = $(target).parents('li');
        }
        if (!target.length || e.target.tagName === 'A') {
            return;
        }
        target.addClass('active').siblings('li').removeClass('active');
        classes = target.attr('class').match(/\d+(?=_)/g);
        that.island_x = params.island_x = ~~classes[0];
        that.island_y = params.island_y = ~~classes[1];
        that.current_town_id = ~~classes[2];
        that.booty_researched = params.booty_researched = ~~classes[3];
        that.trade_office = params.trade_office = ~~classes[4];
        that.getFarmTowns(params);
    });
};
WndHandlerFarmTownOverviews.prototype.pillageClick = function () {
    this.pillage_menu = true;
    this.invalideData(true);
    if (this.farm_towns != null) {
        this.selectAllFarms(true);
        this.changeMaxResources();
    }
};
WndHandlerFarmTownOverviews.prototype.initializeResourcesCounter = function (towns) {
    var that = this;
    var step = 5000;
    var town;
    var town_id;
    for (town_id in towns) {
        if (!towns.hasOwnProperty(town_id)) {
            continue;
        }
        town = towns[town_id];
        ITowns.setResources(town.resources, town.id);
        ITowns.setProduction(town.production, town.id);
        ITowns.setStorage(town.storage_volume, town.id);
    }
    $(document).everyTime(step, 'WndHandlerFarmTownOverviews::initializeResourcesProductionCounter::timer', function () {
        that.updateResources();
    });
};
WndHandlerFarmTownOverviews.prototype.updateResources = function () {
    var resources = ['wood', 'stone', 'iron'],
        towns = ITowns.getTowns(),
        town, res, i, elem;
    var town_id;
    for (town_id in towns) {
        if (!towns.hasOwnProperty(town_id)) {
            continue;
        }
        ITowns.updateResourceCounter(town_id);
        town = towns[town_id];
        res = town.resources();
        i = resources.length;
        while (i--) {
            var id = resources[i];
            elem = $('#fto_town_list li.town' + town_id + ' span.fto_resource_count.' + id + ' span.count');
            if (!elem.length) {
                continue;
            }
            if (parseInt(res[id], 10) >= parseInt(res.storage, 10)) {
                elem.addClass('town_storage_full');
                elem.html(res.storage);
            } else {
                if (elem.hasClass('town_storage_full')) {
                    elem.removeClass('town_storage_full');
                }
                elem.html(res[id]);
            }
        }
    }
};

function WndHandlerChooseDirection(wndhandle) {
    this.wnd = wndhandle;
    this.updateMap = false;
    window.Layout = {};
}
WndHandlerChooseDirection.inherits(WndHandlerDefault);
WndHandlerChooseDirection.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        maxHeight: 240,
        maxWidth: 1200,
        height: 210,
        width: 480,
        resizable: false,
        minimizable: false,
        title: 'You do not have a city in your possession!'
    };
    return ret;
};
WndHandlerChooseDirection.prototype.onInit = function (title, UIopts) {
    var that = this;
    this.bg = $('<div id="blockbox_bg"></div>');
    this.bg.click(function () {
        that.wnd.close();
    }).appendTo('body').show();
    this.wnd.setContent($('#chooseDirection').html());
    this.wnd.getJQElement().find('#button_close').click(function () {
        that.wnd.close();
    });
    this.wnd.getJQElement().find('#button_choose_world').click(function () {
        window.location = $('#master_url').val() + '/start/index?action=select_new_world';
    });
    return true;
};
WndHandlerChooseDirection.prototype.onClose = function () {
    this.bg.remove();
    return true;
};

WndHandlerShowOnLogin = function (wndhandle) {
    this.wnd = wndhandle;
    this.index = 0;
    this.thingsToShow = [];
    var that = this;
    this.renderItem = function (data, full) {
        if (!data) {
            this.wnd.close();
            return;
        }
        var content, info = tmpl2(GameData.ShowOnLoginTemplate, this),
            type = data.type.split('.');
        switch (type[0]) {
        case 'gift':
            content = tmpl2(GameData.GiftTemplate, data);
            break;
        case 'hint':
            content = data.text || data.html;
            break;
        default:
            content = data.text || data.html || '';
            break;
        }
        if (full) {
            this.wnd.setContent2(String.prototype.concat.call('<div id="showonlogin_wrapper">', content, '</div><div id="showonlogin_info">', info, '</div>'));
        } else {
            $('#showonlogin_wrapper').stop().fadeOut(300, function () {
                $(this).html(content).fadeIn(300, function () {
                    var diff, def_height = that.getDefaultWindowOptions().height;
                    this.removeAttribute('style');
                    if (that.thingsToShow.length > 1 && (diff = $('#showonlogin_wrapper').height() - 240) > 0) {
                        that.wnd.setHeight(def_height + diff);
                    } else {
                        that.wnd.setHeight(def_height);
                    }
                });
            });
            $('#showonlogin_info').html(info);
        }
        this.wnd.setTitle(data.name || data.title);
    };
    this.switchCallback = function () {
        that.renderItem(that.thingsToShow[that.index]);
        that.wnd.toTop();
    };
};
WndHandlerShowOnLogin.inherits(WndHandlerDefault);
WndHandlerShowOnLogin.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        height: 360,
        width: 490,
        resizable: false,
        minimizable: true,
        title: ''
    };
    return ret;
};
WndHandlerShowOnLogin.prototype.onRcvData = function (data) {
    var item, that = this,
        g = GPEvents.global;
    this.thingsToShow = this.thingsToShow.concat(data);

    function compare(a, b) {
        a = a.priority || 0;
        b = b.priority || 0;
        if (a > b) {
            return -1;
        } else if (a < b) {
            return 1;
        }
        return 0;
    }
    item = this.thingsToShow.sort(compare)[this.index];
    this.renderItem(item, true);
    this.wnd.getJQElement().unbind('click.showonlogin').bind({
        'click.showonlogin': function (e) {
            that.clickHandler(e);
        }
    });
    g.bind(g.town.town_switch, that.switchCallback);
};
WndHandlerShowOnLogin.prototype.clickHandler = function (e) {
    var target = e.target;
    if (!target.href && target.className == "middle") {
        target = target.parentNode.parentNode.parentNode;
    }
    var item, href = target.href ? target.href.split(/#/).reverse()[0] : null,
        id, that = this,
        opt, type = this.thingsToShow[this.index].type.split('.');
    if (href && href.match('accept_gift')) {
        href = href.match(/[a-z_]+(?=_)|\d+/g);
        opt = href[2];
        id = href[1];
        href = href[0];
    }
    switch (href) {
    case 'accept_gift':
        if ($(target).hasClass('disabled')) {
            break;
        }
        Layout.ajaxPost('gift', 'accept', {
            "gift_id": ~~id,
            "option": opt
        }, true, function (data) {
            var rew_type = that.thingsToShow[that.index].type.split('.')[1],
                rew_lvl = that.thingsToShow[that.index].level != 'undefined' ? that.thingsToShow[that.index].level : 0;
            if (rew_type != 'undefined') {
                Kontagent.trackEvent({
                    userId: Game.uid,
                    eventName: rew_type + '_' + rew_lvl + '_opt_' + opt,
                    subtype1: Game.market_id,
                    subtype2: Game.world_id,
                    subtype3: 'Rewards'
                }, function () {});
            }
            that.thingsToShow.remove(that.index);
            that.index = Math.min(that.index, that.thingsToShow.length - 1);
            that.renderItem(that.thingsToShow[that.index], true);
        });
        break;
    case 'next':
        this.index === this.thingsToShow.length - 1 ? this.index = 0 : this.index++;
        this.renderItem(this.thingsToShow[this.index]);
        break;
    case 'prev':
        this.index === 0 ? this.index = this.thingsToShow.length - 1 : this.index--;
        this.renderItem(this.thingsToShow[this.index]);
        break;
    case 'confirm':
        if (type[0] === 'hint' && type.length > 1) {
            if (type[1] === 'curator' || PlayerHint.confirm(type[1])) {
                if (type[1] === 'curator') {
                    this.thingsToShow[this.index].confirm();
                } else {
                    Layout.player_hint_settings[type.reverse().join('_')] = false;
                }
                this.thingsToShow.remove(this.index);
                this.index = Math.min(this.index, this.thingsToShow.length - 1);
                this.renderItem(this.thingsToShow[this.index], true);
            }
        }
        break;
    }
};
WndHandlerShowOnLogin.prototype.onClose = function () {
    var g = GPEvents.global,
        that = this;
    g.unbind(g.town.town_switch, that.switchCallback);
    return true;
};

function WndHandlerInviteFriends(wndhandle) {
    this.wnd = wndhandle;
    this.selectedSpot = null;
    this.spotData = {};
    this.originalWidth = null;
    this.originalPosition = null;
    this.inviteType = null;
    this.selectionType = null;
    this.controller = 'invite_friends';
    var that = this;

    function selectSpotAuto(spot) {
        Layout.townSwitch(spot.town_id);
        if (!that.originalWidth) {
            that.originalWidth = that.wnd.getWidth();
            that.originalPosition = that.wnd.getPosition();
        }
        that.wnd.setWidth(320);
        that.wnd.setPosition([180, 60]);
        WMap.jumpToPosByCoords(spot.x, spot.y, true);
        $('#invite_splash').hide();
    }
    this.clickHandler = function (e) {
        var target = $(e.target).closest('a.button, a.button_link'),
            href = target.attr('href') || '',
            type, name, id;
        if (!href) {
            return true;
        }
        href = href.split('#').reverse()[0].split('.');
        name = href[1] || null;
        id = href[2] || null;
        target.parents('li').removeClass('transparent').siblings().addClass('transparent');
        switch ((type = href[0])) {
        case 'invite':
            that.inviteType = name;
            if (this.selectedSpot) {
                that.acceptSpot('fixed_spot', that.selectedSpot);
            } else {
                that.wnd.requestContentGet(that.controller, 'invite', null);
            }
            break;
        case 'getReward':
            reward(parseInt(href[1], 10));
            break;
        case 'accept':
            type = $('form#invite_friend input:checked').val();
            if (type === 'fixed_island') {
                var selection = $('form#invite_friend select').val();
                that.selectedSpot = that.spotData[selection];
            }
            that.acceptSpot(type, that.selectedSpot);
            break;
        case 'send_invitation':
            that.sendInvitation();
            break;
        default:
            return true;
        }
        return false;
    };
}
WndHandlerInviteFriends.inherits(WndHandlerDefault);
WndHandlerInviteFriends.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        height: 550,
        width: 800,
        resizable: false,
        minimizable: true,
        yOverflowHidden: true
    };
    return ret;
};
WndHandlerInviteFriends.prototype.onInit = function (title, UIopts, action) {
    var args = UIopts || {};
    args.player_town_id && (this.selectedSpot = args);
    var use_action = action || 'index';
    this.wnd.requestContentGet(this.controller, use_action, args);
    this.restoreWindow();
    return true;
};
WndHandlerInviteFriends.prototype.onRcvData = function (data) {
    var that = this;
    this.restoreWindow();
    if (data.json) {
        if (data.json.clearSelection) {
            this.selectedSpot = null;
        }
        if (data.json.selectedSpot) {
            this.selectedSpot = data.json.selectedSpot;
        }
        if (data.json.spotData) {
            this.spotData = data.json.spotData;
        }
    }
    this.wnd.setContent2(data.html);
    this.wnd.getJQElement().children('div.gpwindow_content').unbind().click(function (e) {
        return that.clickHandler(e);
    });
};
WndHandlerInviteFriends.prototype.acceptSpot = function (selectionType, spot) {
    var that = this;
    this.selectionType = selectionType;
    this.wnd.requestContentPost(this.controller, 'get_credentials', {
        type: this.inviteType,
        spot: spot,
        selection: this.selectionType
    }, function () {
        if (that.inviteType == 'url') {
            Kontagent.trackEvent({
                userId: Game.uid,
                eventName: 'invite_friends_send_via_' + that.inviteType,
                subtype1: Game.market_id,
                subtype2: Game.world_id,
                subtype3: 'invitation'
            }, function () {});
        }
    });
};
WndHandlerInviteFriends.selectSpotOnMap = function (data) {
    var spot = {
        x: data.ix,
        y: data.iy,
        player_town_id: data.player_town_id,
        distance: data.distance,
        number_on_island: data.number_on_island
    };
    var wnd = GPWindowMgr.getOpenFirst(Layout.wnd.TYPE_INVITE_FRIENDS);
    Kontagent.trackEvent({
        userId: Game.uid,
        eventName: 'invite_friends_from_map',
        subtype1: Game.market_id,
        subtype2: Game.world_id,
        subtype3: 'invitation'
    }, function () {});
    Layout.wnd.Create(Layout.wnd.TYPE_INVITE_FRIENDS, 'Invite players', spot);
};
WndHandlerInviteFriends.prototype.sendInvitation = function () {
    Kontagent.trackEvent({
        userId: Game.uid,
        eventName: 'invite_friends_send_via_' + this.inviteType,
        subtype1: Game.market_id,
        subtype2: Game.world_id,
        subtype3: 'invitation'
    }, function () {});
    var root = this.wnd.getJQElement(),
        email = root.find('[name=email]').val(),
        invitee_name = root.find('[name=invitee_name]').val(),
        invitor_name = root.find('[name=invitor_name]').val(),
        that = this;
    this.wnd.requestContentPost(this.controller, 'send_invitation', {
        invitee_name: invitee_name,
        invitor_name: invitor_name,
        email: email,
        spot: this.selectedSpot,
        type: this.inviteType,
        selection: this.selectionType
    }, function () {
        that.wnd.close();
    });
};
WndHandlerInviteFriends.prototype.restoreWindow = function () {
    if (this.originalWidth && this.originalPosition) {
        this.wnd.setWidth(this.originalWidth);
        this.wnd.setPosition(this.originalPosition);
        $('#invite_splash').show();
        this.originalWidth = null;
        this.originalPosition = null;
    }
};
WndHandlerInviteFriends.prototype.clearSelection = function () {
    this.selectedSpot = null;
};

function WndHandlerPresents(wndhandle) {
    this.wnd = wndhandle;
    this.island_x = 0;
    this.island_y = 0;
}
WndHandlerPresents.inherits(WndHandlerDefault);
WndHandlerPresents.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        height: 390,
        width: 468,
        resizable: false,
        fullwindow: true,
        title: 'Pumpkin found'
    };
    return ret;
};
WndHandlerPresents.prototype.onInit = function (title, UIopts, island_x, island_y) {
    var that = this;
    this.island_x = island_x;
    this.island_y = island_y;
    this.wnd.requestContentGet('presents', 'index', {}, function (wnd, data) {});
    return true;
};
WndHandlerPresents.prototype.onRcvData = function (data) {
    this.wnd.setContent2(data.html);
    this.unregisterEventListeners();
    this.registerEventListeners();
    if (data.next_hunting_at) {
        this.wnd.getJQElement().find('div.next_hunting_at').countdown(data.next_hunting_at, {});
    }
    this.wnd.getJQElement().find('div.pumpkin_count').mousePopup(new MousePopup('Collected pumpkins'));
};
WndHandlerPresents.prototype.startPumpkinHunting = function () {
    var that = this;
    this.wnd.requestContentPost('presents', 'startPumpkinHunting', {
        island_x: this.island_x,
        island_y: this.island_y
    }, function (wnd, data) {
        MapPresents.startHunting(data.hunting_ends_at);
        MapPresents.collect(that.island_x, that.island_y);
        MapTiles.updateTownsForCurrentPosition();
        w(that).close();
    });
};
WndHandlerPresents.prototype.registerEventListeners = function () {
    var that = this;
    this.wnd.getJQElement().find('a.start_pumpkin_hunting').unbind().click(function (e) {
        that.startPumpkinHunting();
    });
    GPEvents.global.bind(GPEvents.global.town.town_switch + '.WndHandlerPresents', function () {
        that.wnd.requestContentGet('presents', 'index');
    });
};
WndHandlerPresents.prototype.unregisterEventListeners = function () {
    GPEvents.global.unbind('.WndHandlerPresents');
};

function GPLocalStore(_playerID, useLocalStore) {
    var that = this;
    var playerID = _playerID;
    var pstore_name = '';
    var pstore = null;
    var gstore = null;
    var pstores = {};
    var mem_pstore = {};
    var mem_gstore = {};
    var _conf_inmemory = 300;

    function addThisPstoreToGlobalDataList() {
        pstores[pstore_name] = true;
        gstore.set('localstores', pstores);
    }

    function globalDataListCallback(ok, data) {
        if (ok) {
            var pstores = data;
            if (!pstores[pstore_name]) {
                addThisPstoreToGlobalDataList();
            }
        } else {
            addThisPstoreToGlobalDataList();
        }
    }

    function init(ls_enabled) {
        if (ls_enabled != false && Persist.isAvailable()) {
            pstore_name = 'Grepo2_01Data_p' + playerID;
            pstore = new Persist.Store(pstore_name);
            gstore = new Persist.Store('Grepo2_01Data_global');
            gstore.get('localstores', globalDataListCallback, that);
        }
    }
    this.check = function () {
        if (!useLocalStore) {
            return false;
        }
        return Persist.isAvailable();
    }
    this.getType = function () {
        return Persist.getType();
    }

    function mem_set(_h, _key, _value, _ttl) {
        var row = {
            data: _value,
            valid_until: Timestamp.server() + ~~_ttl
        };
        _h[_key] = row;
    }

    function mem_get(_h, _key) {
        var row = _h[_key];
        if (row) {
            if (row.valid_until >= Timestamp.server()) {
                return row.data;
            } else {
                _h[_key] = undefined;
                delete _h[_key];
                return undefined;
            }
        }
        return undefined;
    }

    function mem_del(_h, _key) {
        var row = _h[_key];
        if (row) {
            _h[_key] = undefined;
            delete _h[_key];
            return true;
        }
        return false;
    }

    function mem_vacuum(_h) {
        var ts = Timestamp.server();
        for (var k in _h) {
            if (_h[k].valid_until < ts) {
                _h[k] = undefined;
                delete _h[k];
            }
        }
    }
    this.set = function (key, value, ttl) {
        ttl = ~~ttl;
        if (ttl < 0) {
            return;
        }
        if (!pstore || (ttl != 0 && ttl <= _conf_inmemory)) {
            mem_set(mem_pstore, key, value, ttl);
            return;
        }
        if (pstore) {
            pstore.set(key, value, null, ttl);
        }
    };
    this.get = function (key, cbfn, param, scope) {
        var data = mem_get(mem_pstore, key);
        if (data) {
            cbfn.call(scope || this, true, data, param);
            return;
        }
        if (pstore) {
            pstore.get(key, cbfn, scope, param);
        } else {
            cbfn.call(scope || this, false, null, param);
        }
    };
    this.del = function (key) {
        if (mem_del(mem_pstore, key)) return;
        if (pstore) pstore.remove(key);
    }
    this.clear = function () {
        mem_pstore = {};
        if (pstore) pstore.clear();
    }
    this.vacuum = function () {
        mem_vacuum(mem_pstore);
        if (pstore) pstore.vacuum();
    }
    this.gset = function (key, value, ttl) {
        if (ttl < 0) {
            return;
        }
        if (!gstore || (ttl != 0 && ttl <= _conf_inmemory)) {
            mem_set(mem_gstore, key, value, ttl);
            return;
        }
        if (gstore) {
            gstore.set(key, value, null, ttl);
        }
    }
    this.gget = function (key, callback, param, scope) {
        var data = mem_get(mem_gstore, key);
        if (data) {
            callback.call(scope || this, true, data, param);
            return;
        }
        if (gstore) {
            try {
                gstore.get(key, callback, scope, param);
            } catch (e) {
                gstore.remove(key);
                callback.call(scope || this, false, null, param);
            }
        } else {
            callback.call(scope || this, false, null, param);
        }
    }
    this.gdel = function (key) {
        if (mem_del(mem_gstore, key)) {
            return;
        }
        if (gstore) {
            gstore.remove(key);
        }
    }
    this.gclear = function () {
        mem_gstore = {};
        mem_pstore = {};
        if (gstore) {
            gstore.clear();
            for (var p in pstores) {
                var tmpstore = new Persist.Store(p);
                if (tmpstore) {
                    tmpstore.clear();
                }
                delete pstores[pstore_name];
                delete tmpstore;
            }
            pstores = {};
            delete pstore;
            delete gstore;
            init();
        }
    }
    this.gvacuum = function () {
        mem_vacuum(mem_gstore);
        if (gstore) gstore.vacuum();
    }
    init(useLocalStore);
};

var Alliance = {
    rights: {},
    title: {},
    action: function (action, params, callback) {
        var fn = function (data) {
                if (typeof callback === 'function') {
                    callback(data);
                }
            };
        if (action == 'save' || action == 'updateName' || action == 'updateFurtherSettings' || action == 'kick' || action == 'cancel_invitation' || action == 'choose_flag') {
            Layout.ajaxPost('alliance', action, params, true, fn);
        } else {
            Layout.ajaxGet('alliance', action, params, true, fn);
        }
    },
    save: function (target, callback) {
        var textarea = $('#ally_' + target + '_textarea');
        if (textarea.length == 0) {
            return;
        }
        var params = {
            field: target,
            value: textarea.val()
        };
        Alliance.action('save', params, callback);
    },
    editAnnounce: function () {
        var callback = function (data) {
                $("#ally_announce_body_content").append('<textarea id="ally_announce_textarea" style="padding:5px 3px;"></textarea>');
                $('#ally_announce_edit').hide();
                $('#ally_announce_bbcodes').show();
                $('#ally_announce_save').show();
                $("#ally_announce_textarea").text(data.value).show();
                $('#ally_announce_body_content .content').hide();
            };
        var params = {
            field: 'announce'
        };
        Alliance.action('get', params, callback);
    },
    saveAnnounce: function () {
        var callback = function (data) {
                $('#ally_announce_edit').show();
                $('#ally_announce_bbcodes').hide();
                $('#ally_announce_save').hide();
                $("#ally_announce_textarea").detach();
                $('#ally_announce_body_content .content').html(data.value).show();
            };
        this.save('announce', callback);
    },
    editImage: function () {
        $('#ally_image').hide();
        $('#ally_image_edit').show();
        $('#ally_profile_save_image').show();
    },
    editProfile: function () {
        var callback = function (data) {
                $("#ally_profile_body_content").append('<textarea id="ally_profile_textarea" style="padding:5px 3px;"></textarea>');
                $('#ally_profile_textarea').autoResizeIt();
                $("#ally_profile_descr").hide();
                $("#profile_bbcodes").show();
                $('#ally_profile_save_desc').show();
                $("#ally_profile_textarea").text(data.value).show();
                $('#ally_profile_body').removeClass('editable');
            };
        var params = {
            field: 'profile'
        };
        Alliance.action('get', params, callback);
    },
    saveProfile: function () {
        var callback = function (data) {
                $("#ally_profile_descr").show();
                $("#profile_bbcodes").hide();
                $('#ally_profile_save_desc').hide();
                $("#ally_profile_textarea").detach();
                $('#ally_profile_body').addClass('editable').find(".ally_profile_content").html(data.value);
            };
        if ($('#image').val() || $('#delete_image:checked').val()) {
            submit_form('alliance_emblem_form', 'alliance', 'updateEmblem');
            $("#ally_image").show();
            $("#ally_image_edit").hide();
            $('#ally_profile_save_image').hide();
        }
        this.save('profile', callback);
    },
    updateName: function () {
        var alliance_name = $('#alliance_name').val();
        var params = {
            name: alliance_name
        };
        Alliance.action('updateName', params, function () {
            WMap.pollForMapChunksUpdate();
        });
        return false;
    },
    updateFurtherSettings: function () {
        var show_contact = $('input:checkbox[name=show_contact_buttons]:checked').val();
        var show_founder_icon = $('input:checkbox[name=show_founder_icon]:checked').val();
        var show_pact_member = $('input:checkbox[name=show_pact_member]:checked').val();
        var block_pact_invitations = $('input:checkbox[name=block_pact_invitations]:checked').val();
        var params = {
            show_contact: (show_contact === 'on' ? true : false),
            show_founder_icon: (show_founder_icon === 'on' ? true : false),
            show_pact_member: (show_pact_member === 'on' ? true : false),
            block_pact_invitations: (block_pact_invitations === 'on' ? true : false)
        };
        Alliance.action('updateFurtherSettings', params, null);
        return false;
    },
    doKickPlayer: function (player_id) {
        Alliance.action('kick', {
            'player_id': player_id
        }, function (data) {
            $('#alliance_player_' + player_id).fadeOut();
            WMap.pollForMapChunksUpdate();
        }.bind(player_id));
    },
    kick_player: function (player_id) {
        var onConfirmationFunction = function () {
                Alliance.doKickPlayer(player_id);
            };
        Layout.showConfirmDialog('Kick out player', 'Are you sure that you want to kick out this player?', onConfirmationFunction);
    },
    cancel_invitation: function (id) {
        Alliance.action('cancel_invitation', {
            id: id
        }, function (data) {
            $('#invitation_' + id).fadeOut();
        }.bind(id));
    },
    chooseFlag: function (params, callback) {
        Alliance.action('choose_flag', params, function (data) {
            WMap.pollForMapChunksUpdate();
            callback(data);
        });
    },
    fetchMemberRights: function () {
        var id, right;
        for (id in Alliance.rights) {
            for (right in Alliance.rights[id]) {
                Alliance.rights[id][right] = $('input[name="rights[' + id.toString() + '][' + right.toString() + ']"]').attr('checked') ? 1 : 0;
            }
        }
        return Alliance.rights;
    },
    fetchMemberTitle: function () {
        var id;
        for (id in Alliance.rights) {
            Alliance.title[id] = $('input[name="title[' + id.toString() + ']"]').attr('value');
        }
        return Alliance.title;
    },
    inviteFriends: function () {
        Kontagent.trackEvent({
            userId: Game.uid,
            eventName: 'invite_friends_from_alliance',
            subtype1: Game.market_id,
            subtype2: Game.world_id,
            subtype3: 'invitation'
        }, function () {});
        Layout.wnd.Create(Layout.wnd.TYPE_INVITE_FRIENDS, 'Invite friends');
    }
};

var AlliancePact = {
    init: function () {
        $('a.confirm').mousePopup(new MousePopup('Confirm the invitation to the pact'));
        $('#ally_pact_list a.cancel').mousePopup(new MousePopup('Dissolve the pact'));
        $('#ally_pact_invitations a.cancel').mousePopup(new MousePopup('Delete the invitation to the pact'));
        $('#ally_pact_list input.pact_visibility').mousePopup(new MousePopup('Show this fellow pact member in your profile'));
        $('#tab_ally_enemies a.cancel').mousePopup(new MousePopup('Dissolve the feud'));
    },
    joinPact: function (inv) {
        Layout.ajaxPost('alliance', 'join_pact', {
            invitation_id: inv
        }, true, function (data) {
            $('#invitation_' + data.invitation_id).remove();
            var invitation_list = $('#tab_ally_pact_invitations ul.game_list');
            if (invitation_list.children().length == 0) {
                invitation_list.append('<li class="odd" style="clear:both;">' + 'No pact requests are available.' + '</li>');
            }
            if (data.end_enmity) {
                $('#enemy_' + data.enmity_id).remove();
                var enemy_list = $('#tab_ally_enemies ul.game_list');
                if (enemy_list.children().length == 0) {
                    enemy_list.append('<li class="odd" style="clear:both;">' + 'No enemies were entered.' + '</li>');
                }
            }
            if ($('#tab_ally_pact_list li.no_entry').length == 1) {
                $('#tab_ally_pact_list ul.game_list').html(data.pact_html);
            } else {
                $('#tab_ally_pact_list ul.game_list').append(data.pact_html);
            }
            $('#ally_pact_list a.cancel').mousePopup(new MousePopup('Dissolve the pact'));
            $('#ally_pact_list input.pact_visibility').mousePopup(new MousePopup('Show this fellow pact member in your profile'));
        });
    },
    cancelInvitation: function (inv) {
        Layout.ajaxPost('alliance', 'cancel_pact_invitation', {
            invitation_id: inv
        }, true, function (data) {
            $('#invitation_' + data.invitation_id).remove();
        });
    },
    cancelPact: function (id) {
        Layout.ajaxPost('alliance', 'cancel_pact', {
            pact_id: id
        }, true, function (data) {
            $('#pact_' + data.pact_id).remove();
            var pact_list = $('#tab_ally_pact_list ul.game_list');
            if (pact_list.children().length == 0) {
                pact_list.append('<li class="odd" style="clear:both;">' + 'No pacts are available.' + '</li>');
            }
        });
    },
    set_pact_visibility: function (pact_id, player_alliance_id, partner_id) {
        var visibility = $('input:checkbox[name=show_partner_' + partner_id + ']:checked').val() === 'on' ? true : false;
        Layout.ajaxPost('alliance', 'update_pact_partner_visibility', {
            pact_id: pact_id,
            alliance_id: player_alliance_id,
            partner_id: partner_id,
            visibility: visibility
        }, true, function () {});
    },
    mark_enemy: function () {
        var name = $('#ally_enemy_form input').val();
        Layout.ajaxPost('alliance', 'mark_enemy', {
            name: name
        }, true, function (data) {
            if ($('#tab_ally_enemies li.no_entry').length == 1) {
                $('#tab_ally_enemies ul.game_list').html(data.enemy_html);
            } else {
                $('#tab_ally_enemies ul.game_list').append(data.enemy_html);
            }
            $('#tab_ally_enemies a.cancel').mousePopup(new MousePopup('Dissolve the feud'));
        });
    },
    end_enmity: function (enmity_id) {
        Layout.ajaxPost('alliance', 'end_enmity', {
            enmity_id: enmity_id
        }, true, function (data) {
            $('#enemy_' + enmity_id).remove();
            var enemy_list = $('#tab_ally_enemies ul.game_list');
            if (enemy_list.children().length == 0) {
                enemy_list.append('<li class="odd no_entry" style="clear:both;">' + 'No enemies were entered.' + '</li>');
            }
        });
    }
}

function BBCode(wnd, wrapper, target) {
    var that = this;
    var Popups = function () {
            var self = this;
            this.names = {};
            this.tableValues = {};
            this.size = function () {
                var callback = function (ev) {
                        that.insert('[size=' + ev.target.id.substr(1) + ']', '[/size]');
                    };
                return openPopup('size', 'bb_sizes', callback);
            };
            this.table = function () {
                function callback() {
                    var rows = parseInt(wrapper.find('input.bb_table_rows').val(), 10),
                        cols = parseInt(wrapper.find('input.bb_table_columns').val(), 10),
                        header = wrapper.find('input.bb_table_header').is(':checked'),
                        str = '',
                        c = cols,
                        i, j, data = self.tableValues;
                    for (i = 0; i < rows; i++) {
                        str += header ? '[**]' : '[*]';
                        for (j = 0; j < cols; j++) {
                            str += (data[i + "x" + j] || '') + (j == cols - 1 ? '' : (header ? '[||]' : '[|]'));
                        }
                        str += (header ? '[/**]' : '[/*]') + '\n';
                        cols = c;
                        header = false;
                    }
                    self.tableValues = {};
                    that.insert('\n[table]\n' + str, '[/table]');
                }
                return openPopup('table', 'bb_table_popup', callback);
            };
            this.font = function (el) {
                return openPopup('font', 'bb_font_chooser', function (e) {
                    var href = e.target.href,
                        className;
                    if (!href) {
                        return;
                    }
                    className = href.split("#").reverse()[0];
                    that.insert('[font=' + className + ']', '[/font]');
                });
            };
            this.town = function () {
                var p = openPopup('town', 'bb_town_chooser');
                p.find('input.bb_town_chooser_town_input').oldautocomplete('/autocomplete', {
                    'minChars': 3,
                    'max': 500,
                    'extraParams': {
                        'what': 'game_town'
                    },
                    'formatItem': function (row) {
                        return row[1] + ' (' + row[2] + ')';
                    }
                }).result(function (result, row) {
                    self.townChosen(row);
                    p.unbind().remove();
                });
                return p;
            };
            this.townChosen = function (row) {
                that.insert('[town]' + row[0], '[/town]', true);
            };
            this.report = function () {
                var p = openPopup('report', 'bb_report_chooser');
                p.find('input.bb_report_chooser_report_input').oldautocomplete('/autocomplete', {
                    'minChars': 3,
                    'max': 500,
                    'extraParams': {
                        'what': 'game_report'
                    },
                    'formatItem': function (row) {
                        return row[1] + ' (' + row[2] + ')';
                    }
                }).result(function (result, row) {
                    self.reportChosen(row);
                    p.unbind().remove();
                });
                return p;
            };
            this.reportChosen = function (row) {
                that.insert('[report]' + row[0], '[/report]', true);
            };
            this.award = function () {
                var p = openPopup('award', 'bb_award_chooser');
                var select = p.find('select');
                p.bind('change', function (ev) {
                    awardChanged.call(ev.target, select, p);
                });
                return p;
            };

            function awardChanged(select_list, p) {
                var type = select_list[0].value;
                var world_id = select_list[1].value;
                var award_id = select_list[2].value;
                if (type == '') {
                    return select_list.filter('select.bb_award_chooser_award_world').hide();
                }
                if (this.name == 'world' && world_id == '') {
                    return select_list.filter('select.bb_award_chooser_award_award').hide();
                }
                if (this.name == 'award' && award_id == '') {
                    return false;
                }
                var params = {
                    type: type
                };
                if (this.name == 'world') {
                    params.world_id = world_id;
                } else if (this.name == 'award') {
                    params.world_id = world_id;
                    params.award_id = award_id;
                }
                Layout.ajaxGet('player_award', 'get_awards', params, true, function (data) {
                    if (data.code) {
                        that.insert('[award]' + data.code, '[/award]', true);
                        select_list.filter("option[value='']").attr('selected', 'selected').end().filter('select.bb_award_chooser_award_world, select.bb_award_chooser_award_award').hide();
                        p.unbind().remove();
                    } else if (data.world_ids) {
                        var worlds = select_list.filter('select.bb_award_chooser_award_world').empty().append($('<option></option>').val('').html('- please select -'));
                        $.each(data.world_ids, function (i, world_id) {
                            worlds.append($('<option></option>').val(world_id).html(world_id));
                        });
                        worlds.show();
                    } else if (data.award_ids) {
                        var awards = select_list.filter('select.bb_award_chooser_award_award').empty().append($('<option></option>').val('').html('- please select -'));
                        $.each(data.award_ids, function (award_id, award_name) {
                            awards.append($('<option></option>').val(award_id).html(award_name));
                        });
                        awards.show();
                    }
                });
            }
            return this;
        }.call({});

    function openPopup(type, classname, callback) {
        var p = $('#bbcode_popups div.' + classname).clone().css('display', 'block').appendTo(wrapper);
        Popups.names[type] = classname;
        p.bind({
            change: function (e) {
                if (type === "table") {
                    var target = e.target,
                        className = target.className || "",
                        targetType = className.split("_");
                    if (targetType[0] == "bbinput" && target.tagName == "INPUT") {
                        Popups.tableValues[targetType[1]] = target.value;
                    }
                    generateTablePreview(p);
                    positionPopup(wrapper.find('a.table'), p);
                }
            },
            keydown: function (ev) {
                var kc = ev.keyCode;
                if (kc == 27) {
                    p.unbind().remove();
                    ev.preventBubble();
                } else if (kc == 13) {
                    if (typeof callback == 'function') {
                        callback(ev);
                    }
                    p.unbind().remove();
                }
            },
            keyup: function (ev) {
                var target = ev.target;
                if (type === 'table' && target.className.match(/rows|columns/)) {
                    generateTablePreview(p);
                    positionPopup(wrapper.find('a.table'), p);
                }
            },
            click: function (ev) {
                var target = ev.target,
                    tag = target.tagName,
                    classname;
                if (tag === 'INPUT' || !callback) {
                    return;
                } else if (tag === 'A' && (classname = target.className) && classname.match('cancel')) {
                    return p.unbind().remove();
                }
                callback(ev);
                p.unbind().remove();
            }
        });
        return p;
    }

    function positionPopup(button, popup) {
        var width, pwidth, pos = button.position();
        pos.top += 22 + 7;
        width = popup.width() + 18;
        pwidth = wrapper.parent().width();
        pos.left += pwidth < pos.left + width ? pwidth - (pos.left + width) : 0;
        popup.css(pos);
    }

    function generateTablePreview(popup) {
        var parent = popup.find('.bb_table_preview'),
            rows = parseInt(popup.find('input.bb_table_rows').val(), 10) || 1,
            cols = parseInt(popup.find('input.bb_table_columns').val(), 10) || 1,
            header = popup.find('input.bb_table_header').is(':checked'),
            ex_rows = parent.find('tr'),
            ex_cells, i, j, row, cell, maxRows = 5,
            maxCols = 5;
        parent.empty();
        parent = $('<table />').appendTo(parent);
        for (i = 0; i < rows; i++) {
            row = $(ex_rows[i] ? ex_rows[i] : '<tr />').appendTo(parent);
            if (i == 0) {
                row[header ? "addClass" : "removeClass"]("bb_table_withheader");
            }
            ex_cells = row.find('td');
            row.empty();
            for (j = 0; j < cols; j++) {
                cell = '<td><span class="grepo_input"><span class="left"><span class="right"><input class="bbinput_' + i + "x" + j + '" /></span></span></span></td>';
                if (j == maxCols && i == maxRows) {
                    cell = '<td></td>';
                } else if (j == maxCols || i == maxRows) {
                    cell = '<td class="' + (i == maxRows ? 'last-row-cell' : '') + '">...</td>';
                }
                $(ex_cells[j] ? ex_cells[j] : $(cell)).appendTo(row);
                if (j == maxCols) {
                    break;
                }
            }
            if (i == maxRows) {
                break;
            }
        }
    }

    function bindLinks() {
        wrapper.click(function (ev) {
            var name, start, end, target = ev.target;
            if (target.tagName !== 'A') {
                return;
            }
            name = target.className;
            if (name == 'confirm' || name == 'cancel') {
                return false;
            } else if (name == 'b' || name == 'i' || name == 's' || name == 'u' || name == 'url' || name == 'img' || name == 'player' || name == 'ally' || name == 'island' || name == 'center') {
                start = end = name;
            } else if (name == 'quote') {
                start = name + '=Author';
                end = name;
            } else if (name == 'color') {
                that.colorpicker = new BBCodeColorPicker(target, function (value) {
                    that.insert('[color=#' + value + ']', '[/color]');
                });
            }
            if (start && end) {
                return that.insert('[' + start + ']', '[/' + end + ']');
            }
            if (typeof Popups[name] !== 'function') {
                return false;
            }
            var popup, classname = Popups.names[name];
            if (classname && (popup = wrapper.find('div.' + classname)).length) {
                popup.unbind().remove();
            } else {
                popup = Popups[name]();
                positionPopup($(target), popup);
                if (name == "table") {
                    generateTablePreview(popup);
                    positionPopup($(target), popup);
                }
            }
        });
    }
    this.insert = function (start_tag, end_tag, force_place_outside) {
        var input = wnd.getJQElement().find(target);
        var scroll_pos = input.scrollTop;
        var ins_text;
        input.focus();
        input = input[0];
        if (typeof document.selection != 'undefined') {
            var range = document.selection.createRange();
            ins_text = range.text;
            range.text = start_tag + ins_text + end_tag;
            range = document.selection.createRange();
            if (ins_text.length > 0 || force_place_outside) {
                range.moveStart('character', start_tag.length + ins_text.length + end_tag.length);
            } else {
                range.move('character', -end_tag.length);
            }
            range.select();
        } else if (typeof input.selectionStart != 'undefined') {
            var start = input.selectionStart;
            var end = input.selectionEnd;
            ins_text = input.value.substring(start, end);
            input.value = input.value.substr(0, start) + start_tag + ins_text + end_tag + input.value.substr(end);
            var pos;
            if (ins_text.length > 0 || force_place_outside) {
                pos = start + start_tag.length + ins_text.length + end_tag.length;
            } else {
                pos = start + start_tag.length;
            }
            input.selectionStart = pos;
            input.selectionEnd = pos;
        }
        input.scrollTop = scroll_pos;
        return false;
    };
    bindLinks();
}

var BuildingAcademy = {
    order_id_to_finish: null,
    confirm_finish_for_gold_dialog: null,
    confirm_building_cancel_dialog: null,
    orders: null,
    full_queue: false,
    researches: null,
    research_points: null,
    finish_for_gold_dialog_title: 'Reduce the research time by half',
    finish_for_gold_dialog_text: null,
    onConfirm: "BuildingAcademy.finishForGold(w(this))",
    onRevert: "BuildingAcademy.revertResearch(w(this))",
    wnd: null,
    revert_research_id: null,
    revert_menu: false,
    handleEvents: function (event, data) {
        if (event.type == GPEvents.global.town.resources_update) {
            if (!BuildingAcademy.revert_menu) {
                BuildingAcademy.updateResearchPopups();
            }
        }
    },
    startCountdown: function () {
        if (this.orders.length > 0) {
            var building_order_current = $('#academy_tasks_0 div.academy_task_time');
            building_order_current.countdown(this.orders[0].to_be_completed_at, {});
            building_order_current.bind('finish', function () {
                BuildingAcademy.reloadContent(BuildingAcademy.wnd);
            });
        }
    },
    initializeMousePopups: function () {
        $('a.academy_tasks_cancel').mousePopup(new MousePopup('<h4>' + 'Cancel' + '</h4>'));
        if (this.orders.length > 0) {
            $.each(this.orders, function (i, order) {
                $('#academy_tasks_' + i + ' img.academy_tasks_image').mousePopup(new MousePopup('<h4>' + order.name + '</h4>'));
            });
        }
    },
    confirm_finish_for_gold: function (order_id, wnd, button) {
        var disabled = 'disabled',
            confirm, cancel, that = this;
        button = $(button);
        if (button.hasClass(disabled)) {
            return;
        }
        if (wnd) {
            this.wnd = wnd;
        }
        button.addClass(disabled);
        confirm = function () {
            that.finishForGold(order_id);
        };
        if (Layout.show_confirmation_popup) {
            cancel = function () {
                button.removeClass(disabled);
            };
            Layout.showConfirmDialog(BuildingAcademy.finish_for_gold_dialog_title, BuildingAcademy.finish_for_gold_dialog_text, confirm, null, cancel, null, null, 'Do not show this window again');
        } else {
            confirm();
        }
    },
    finishForGold: function (order_id) {
        BuildingAcademy.action('finish_for_gold', order_id, BuildingAcademy.wnd, true, function () {
            var i = 0,
                research_type = '';
            for (i; i < BuildingAcademy.orders.length; i++) {
                if (BuildingAcademy.orders[i].id == order_id) {
                    research_type = BuildingAcademy.orders[i].research_type;
                    break;
                }
            }
            Kontagent.trackEvent({
                userId: Game.uid,
                eventName: 'fin_res_' + research_type,
                subtype1: Game.market_id,
                subtype2: Game.world_id,
                subtype3: 'buy_pa_feature'
            }, function () {});
            var ev = GPEvents.global;
            ev.trigger(ev.spend_gold);
        });
    },
    action: function (action, id, wnd, show_loader, callback) {
        if (show_loader) {
            Layout.ajaxPost('building_academy', action, {
                id: id
            }, true, function (data) {
                wnd.setContent2(data.html);
                if (callback) {
                    callback();
                }
            });
        } else {
            wnd.requestContentPost(GameData.buildings.academy.controller, action, {
                id: id
            });
        }
    },
    reloadContent: function (wnd) {
        Layout.ajaxGet('building_academy', 'load_content', {}, true, function (data) {
            $('#academy_tech_bg').parent().html(data.html);
        });
    },
    confirm_revert: function (research_id, wnd) {
        this.revert_research_id = research_id;
        this.wnd = wnd;
        var title = 'Reset the research';
        var text = 'This action will cost you one culture point! Do you really want to reset this research?';
        Layout.showConfirmDialog(title, text, function () {
            BuildingAcademy.revertResearch(wnd);
        });
    },
    revertResearch: function (wnd) {
        BuildingAcademy.wnd.clearMenu();
        BuildingAcademy.action('revert_research', BuildingAcademy.revert_research_id, BuildingAcademy.wnd, false);
        wnd.close();
    },
    updateResearchPopups: function () {
        var research_name;
        var res = ITowns.getTown(Game.townId).resources();
        var current_production = ITowns.getTown(Game.townId).production();

        function calculateResources() {
            jQuery.each(reqInQuestion, function (reqId, req) {
                if (reqId == 'research_points' && req.amount > BuildingAcademy.research_points) {
                    not_possible = true;
                    enough_points = false;
                }
                if (req.amount > res[reqId]) {
                    not_possible = true;
                    if (reqId != 'time' && reqId != 'research_points') {
                        enough_resources = false;
                        if (current_production[reqId] > 0) {
                            var time = parseInt(3600 * parseFloat((req.amount - res[reqId]) / current_production[reqId]), 10);
                            if (time_to_build < time && time > 0) {
                                time_to_build = time;
                            }
                        }
                    }
                }
                result += '<img src="' + Game.img() + '/game/res/' + reqId + '.png" alt="' + req.name + '" />';
                result += '<span' + ((req.amount > res[reqId]) || (reqId == 'research_points' && enough_points == false) ? ' style="color:#B00"' : '') + '>' + req.amount + '</span>';
            });
        }
        for (research_name in BuildingAcademy.researches) {
            if (!BuildingAcademy.researches.hasOwnProperty(research_name)) {
                continue;
            }
            var enough_resources = true;
            var enough_points = true;
            var not_possible = false;
            var time_to_build = 0;
            var research_data = BuildingAcademy.researches[research_name].research;
            var result = '';
            var reqInQuestion = {
                wood: {
                    name: 'Wood',
                    amount: research_data.needed_resources.wood
                },
                stone: {
                    name: 'Rock',
                    amount: research_data.needed_resources.stone
                },
                iron: {
                    name: 'Silver coins',
                    amount: research_data.needed_resources.iron
                },
                research_points: {
                    name: 'Research points',
                    amount: research_data.research_points
                },
                time: {
                    name: 'Time',
                    amount: BuildingAcademy.researches[research_name].research_time
                }
            };
            result += '<div class="academy_popup">';
            result += '<h4>' + research_data.name + '</h4>';
            result += '<p style="width: 320px;">' + research_data.description + '</p>';
            if (BuildingAcademy.researches[research_name].isResearched) {
                not_possible = true;
                result += '<h5>' + 'Already researched.' + '</h5>';
            } else if (BuildingAcademy.researches[research_name].inProgress) {
                not_possible = true;
                result += '<h5>' + 'Currently being researched.' + '</h5>';
            } else {
                calculateResources();
                result += '<br/>';
                var missing_buildings = BuildingAcademy.researches[research_name].missing_buildings;
                if (!isEmpty(missing_buildings)) {
                    not_possible = true;
                    var building;
                    result += '<h5>' + 'Building dependencies:' + '</h5>';
                    for (building in missing_buildings) {
                        result += '<span class="requirement">' + building + ' ' + missing_buildings[building] + '</span><br />';
                    }
                }
                if (!enough_resources) {
                    not_possible = true;
                    result += '<span class="requirement">' + 'Not enough resources.' + '</span><br />' + '<span class="requirement">' + 'Enough resources: %1.'.replace('%1', formatDateTimeNice(Timestamp.server() + time_to_build, false)) + '</span><br />';
                }
                if (BuildingAcademy.full_queue) {
                    not_possible = true;
                    result += '<span class="requirement">' + 'No further orders possible.' + '</span><br />';
                }
            }
            result += '</div>';
            if (not_possible) {
                $('#academy_research_' + research_name).find('span.academy_up').remove();
            } else {
                if ($('#academy_research_' + research_name).find('span.academy_up').length <= 0) {
                    $('#academy_research_' + research_name).append('<a href="javascript:void(0)" onclick="BuildingAcademy.action(\'research\', \'' + research_name + '\', w(this)); return false;"><span class="academy_button academy_up"></span></a>');
                }
            }
            $('#academy_research_' + research_name).mousePopup(new MousePopup(result));
        }
    }
};

var BuildingAnimation = {
    init: function () {
        var animations = $("[class^='index_'][class$='building_animation']");
        $.each(animations, function (id, elem) {
            var background_image = $(elem).css('background-image');
            if (background_image.indexOf('worker1_sprite.png') !== -1) {
                BuildingAnimation.animate(elem, 10, 27);
            } else if (background_image.indexOf('worker2_sprite.png') !== -1) {
                BuildingAnimation.animate(elem, 17, 29);
            } else if (background_image.indexOf('worker3_sprite.png') !== -1) {
                BuildingAnimation.animate(elem, 21, 26);
            } else if (background_image.indexOf('worker4_sprite.png') !== -1) {
                BuildingAnimation.animate(elem, 21, 26);
            } else if (background_image.indexOf('worker5_sprite.png') !== -1) {
                BuildingAnimation.animate(elem, 9, 18);
            } else if (background_image.indexOf('worker6_sprite.png') !== -1) {
                BuildingAnimation.animate(elem, 10, 27);
            }
        });
    },
    animate: function (elem, images_count, height) {
        var i = 0;

        function redraw() {
            $(elem).css('background-position', '0 ' + (i * height) + 'px');
            if (images_count + i == 0) {
                i = 0;
            } else {
                i--;
            }
        }
        window.setInterval(redraw, 100);
    }
}

var BuildingFarm = {
    wnd: null,
    next_militia_available: null,
    timer: null,
    militia_time: null,
    init: function (wnd, next_mil, militia_time) {
        this.wnd = wnd;
        this.militia_time = militia_time;
        this.next_militia_available = next_mil;
        if (BuildingFarm.timer != undefined) {
            window.clearInterval(BuildingFarm.timer = null);
        }
        var that = this;
        if (this.next_militia_available > 0) {
            var militia_to_go = $('#militia_to_go');
            militia_to_go.countdown(this.next_militia_available);
            militia_to_go.unbind().bind('finish', function () {
                that.wnd.requestContentGet('building_farm', 'index', {
                    bar: 'bar'
                });
            });
            BuildingFarm.setupBarUpdateTimer();
        }
    },
    confirm_request_militia: function () {
        var title = 'Militia',
            text = 'Should the militia now be enlisted?';
        Layout.showConfirmDialog(title, text, function () {
            BuildingFarm.action_request_militia();
        });
    },
    action_request_militia: function () {
        this.wnd.requestContentPost('building_farm', 'request_militia', {});
    },
    setupBarUpdateTimer: function () {
        BuildingFarm.timer = window.setInterval(BuildingFarm.update, 1E4);
    },
    update: function () {
        var width = Math.round((1 - ((BuildingFarm.next_militia_available - Timestamp.now()) / BuildingFarm.militia_time)) * 280);
        $('#farm_militia div.storage_res').width(width);
    }
}

var BuildingMain = {
    special_buildings_combined_group: null,
    buildings: null,
    full_queue: false,
    confirm_finish_for_gold_dialog: null,
    confirm_building_cancel_dialog: null,
    tear_down_menu: false,
    wnd: null,
    initializeMousePopups: function () {
        $('#main_show_not_possible').mousePopup(new MousePopup('Show buildings for which the construction requirements have not yet been fulfilled'));
    },
    handleEvents: function (event, data) {
        if (event.type == GPEvents.global.town.resources_update) {
            if (!BuildingMain.tear_down_menu) {
                BuildingMain.buildingMousePopup();
            }
        }
    },
    replaceWithForbiddenButton: function (building_name, flag_special_building) {
        var html_str = '';
        if (flag_special_building === true) {
            $('#special_building_' + building_name).css('background-image', 'url(http://cdn.grepolis.com/images/game/main/' + building_name + '_passive.png)');
            $('#special_building_' + building_name).removeAttr('onclick');
        } else {
            if ($('#building_main_not_possible_button_' + building_name).length <= 0) {
                html_str = '<div class="build_grey small bold" id="building_main_not_possible_button_' + building_name + '">' + 'Not possible' + '</div>';
                $('#building_main_' + building_name + ' a.build').remove();
                $('#building_main_' + building_name + ' div.building').append($(html_str));
            }
        }
    },
    replaceWithGrantedButton: function (building_name, building, flag_special_building, first_level) {
        var html_str = '';
        if (flag_special_building === true) {
            $('#special_building_' + building_name).css('background-image', 'url(http://cdn.grepolis.com/images/game/main/' + building_name + '.png)');
            var temp_building = document.getElementById('special_building_' + building_name);
            if (temp_building) {
                temp_building.onclick = function () {
                    BuildingMain.buildBuilding(building_name);
                    return false;
                };
            }
        } else {
            if ($('#building_main_' + building_name + ' a.build').length <= 0) {
                var exp_str = first_level ? 'Build' : 'Expansion to %1'.replace('%1', building.next_level);
                html_str = '<a href="#" onclick="BuildingMain.buildBuilding(\'' + building_name + '\'); return false;" class="build small">' + exp_str + '</a>';
                $('#building_main_not_possible_button_' + building_name).remove();
                $('#building_main_' + building_name + ' div.building').append($(html_str));
            }
        }
    },
    tearDown: function (building) {
        var params = {
            'building': building
        };
        this.callAction('tear_down', params);
    },
    finishForGold: function (order_id) {
        var params = {
            'order_id': order_id
        };
        this.callAction('finish_for_gold', params);
    },
    buildBuilding: function (building, level) {
        var params = {
            'building': building,
            'level': level
        };
        if (BuildOrders.order_id_to_finish) {
            params.order_id = BuildOrders.order_id_to_finish;
        }
        this.callAction('build', params);
    },
    cancelBuilding: function () {
        this.callAction('cancel');
    },
    buildingFinished: function () {
        this.callAction('building_finished');
    },
    callAction: function (action, params) {
        params = params || {};
        params.wnd_main = this.isMainWindowOpen();
        params.wnd_index = this.isIndexWindowOpen();
        if (params.wnd_main || params.wnd_index) {
            var that = this,
                tear_down_active = this.tear_down_menu;
            var callback = function (data) {
                    if (data.index) {
                        that.reloadIndexWindow(data);
                        var ev = GPEvents.global;
                        ev.trigger(ev.spend_gold);
                    }
                    if (data.main) {
                        that.reloadMainWindow(data);
                        if (tear_down_active) {
                            BuildingMain.tearDownClick();
                        }
                    }
                    BuildingMain.logAction(action, params);
                };
            if (action === 'tear_down' || action === 'finish_for_gold' || action === 'build' || action === 'cancel') {
                Layout.ajaxPost('building_main', action, params, true, callback);
            } else {
                Layout.ajaxGet('building_main', action, params, true, callback);
            }
        }
    },
    logAction: function (action, params) {
        switch (action) {
        case 'finish_for_gold':
            var i = 0,
                building_type = '';
            for (i; i < BuildOrders.orders.length; i++) {
                if (BuildOrders.orders[i].id == params.order_id) {
                    building_type = BuildOrders.orders[i].building_type;
                    break;
                }
            }
            var event_postfix = ABTests.getKontagentAbcEventPostfix(GameData.abctest_time_reduction);
            if (event_postfix != "") {
                Kontagent.trackEvent({
                    userId: Game.uid,
                    eventName: 'finish_for_gold' + event_postfix,
                    subtype1: 'FunnelData',
                    subtype2: 'time_reduction_btn_2'
                }, function () {});
            }
            Kontagent.trackEvent({
                userId: Game.uid,
                eventName: 'finish_' + building_type,
                subtype1: Game.market_id,
                subtype2: Game.world_id,
                subtype3: 'buy_pa_feature'
            }, function () {});
            break;
        case 'build':
            Kontagent.trackEvent({
                userId: Game.uid,
                eventName: params.building,
                level: params.level,
                subtype1: Game.market_id,
                subtype2: Game.world_id,
                subtype3: 'build_building'
            }, function () {});
            break;
        }
    },
    reloadMainWindow: function (data) {
        Layout.buildingWindow.getWnd().setContent(data.main.html);
    },
    isBuildFromTownIndexEnabled: function () {
        if (this.isIndexWindowOpen()) {
            return Layout.townIndex.getWnd().getHandler().buildFromTownIndexEnabled;
        }
        return false;
    },
    isMainWindowOpen: function () {
        return Layout.buildingWindow.getWnd() != undefined && Layout.buildingWindow.getWnd().getHandler().currentBuilding == 'main' ? 1 : 0;
    },
    isIndexWindowOpen: function () {
        return Layout.townIndex.getWnd() != undefined ? 1 : 0;
    },
    reloadIndexWindow: function (data) {
        data.index.build_from_town_index_enabled = data.build_from_town_index_enabled;
        if (data.build_from_town_index_enabled) {
            data.build_from_town_index = data.build_from_town_index_enabled;
            BuildOrders.init(data.index.json_orders, data.index.available_gold, data.index.finish_building_order_cost, data.remaining_reductions_until_free, data.is_finish_order_campaign_active);
            data.index.orders = tmpl2(GameData.BuildingOrdersTemplate, {
                'building_orders': ITowns.getTown(Game.townId).buildingOrdersETC()
            });
        }
        Layout.townIndex.getWnd().getHandler().refresh(data.index);
    },
    reloadContent: function () {
        var wnd = Layout.buildingWindow.getWnd();
        if (wnd) {
            Layout.buildingWindow.refresh();
        }
    },
    bindClickeventsOnUpgradeAndTearDownButtons: function () {
        $('#upgrade_buildings').click(function () {
            $('#buildings a.tear_down,#buildings div.tear_down').hide();
            $('#buildings a.build').show();
            BuildingMain.buildingMousePopup();
            BuildingMain.tear_down_menu = false;
        });
        $('#tear_down_buildings').click(BuildingMain.tearDownClick);
    },
    tearDownClick: function () {
        $('#buildings a.build').hide();
        $('#buildings a.tear_down,#buildings div.tear_down').show();
        BuildingMain.tearDownMousePopup();
        BuildingMain.tear_down_menu = true;
    },
    buildingMousePopup: function () {
        var data, building, building_name, first_level;
        for (building_name in BuildingMain.buildings) {
            if (!BuildingMain.buildings.hasOwnProperty(building_name)) {
                continue;
            }
            building = BuildingMain.buildings[building_name];
            data = getUpgradeBuildingData(building, BuildingMain.full_queue);
            first_level = building.level == 0 ? true : false;
            if (data[1] === false) {
                if (data[2] === true) {
                    BuildingMain.replaceWithForbiddenButton(building_name, false, first_level);
                } else {
                    BuildingMain.replaceWithGrantedButton(building_name, building, false, first_level);
                }
            }
            $('#building_main_' + building_name).mousePopup(new MousePopup(data[0]));
        }
        for (building_name in BuildingMain.special_buildings_combined_group) {
            if (!BuildingMain.special_buildings_combined_group.hasOwnProperty(building_name)) {
                continue;
            }
            building = BuildingMain.special_buildings_combined_group[building_name];
            data = getUpgradeBuildingData(building, BuildingMain.full_queue);
            if (data[1] === false) {
                if (data[2] === true) {
                    BuildingMain.replaceWithForbiddenButton(building_name, false, false);
                } else {
                    BuildingMain.replaceWithGrantedButton(building_name, building, false, false);
                }
            }
            $('#special_building_' + building_name).mousePopup(new MousePopup(data[0]));
        }
    },
    tearDownMousePopup: function () {
        var building_name;
        for (building_name in BuildingMain.buildings) {
            if (!BuildingMain.buildings.hasOwnProperty(building_name)) {
                continue;
            }
            var building = BuildingMain.buildings[building_name];
            var txt = '<h5>' + building.name + '</h5>';
            txt += '<img src="' + Game.img() + '/game/res/pop.png" alt="' + 'Food' + '"/>';
            txt += 'Free population that will become available:' + ' ' + building.pop_tear_down + '<br/>';
            txt += '<img src="' + Game.img() + '/game/res/time.png" alt="' + 'Time' + '"/>';
            txt += 'Demolition time:' + ' ' + building.tear_down_time + '<br/>';
            $('#building_main_' + building_name).mousePopup(new MousePopup(txt));
        }
    }
};

function WndHandlerMarket(wndhandle) {
    this.wnd = wndhandle;
    this.market = null;
    this.actionOwnOffer = function (data) {
        this.wnd.setContent2(data.html);
        this.market = new BuildingMarket('create', data.json);
    };
    this.actionList = function (data) {
        if (data.html) {
            this.wnd.setContent2(data.html);
            this.market = new BuildingMarket('market', data.json);
        } else if (data.offers) {
            $('#building_market_endless_scroll').empty().append(data.offers);
        }
    };
}
WndHandlerMarket.inherits(WndHandlerBuilding);
WndHandlerMarket.prototype.onRcvData = function (data, controller, action) {
    switch (action) {
    case 'own_offer':
        this.actionOwnOffer(data);
        break;
    case 'index':
        this.actionList(data);
        this.updateProgressbar(data.json.available_capacity, data.json.max_capacity);
        break;
    case 'offer_list':
        this.actionList(data);
        break;
    case 'create_offer':
        $('#market_offers').empty().append(data.html);
        if (data.json) {
            this.updateProgressbar(data.json.available_capacity, data.json.max_capacity);
        }
        break;
    case 'delete_offer':
        $('#market_offers').empty().append(data.html);
        if (data.json) {
            this.updateProgressbar(data.json.available_capacity, data.json.max_capacity);
        }
        break;
    case 'accept_offer':
        $('#building_market_endless_scroll').empty().append(data.offers);
        this.updateProgressbar(data.available_capacity);
        break;
    default:
    }
};
WndHandlerMarket.prototype.updateProgressbar = function (available, max) {
    if (!isNumber(available)) {
        return;
    }
    if (!isNumber(max)) {
        max = this.maxcap;
    } else {
        this.maxcap = max;
    }
    $('#trade_capacity_progress').stop().animate({
        'width': available / max * 100 + '%'
    }).next().next().html(available);
};
WndHandlerMarket.prototype.onClose = function () {
    this.market = null;
    delete this.market;
    return true;
};

function BuildingMarket(type, data) {
    var offset = 0,
        that = this,
        last_id, params = {
            offer_type: '',
            demand_type: '',
            max_delivery_time: 0,
            max_ratio: 0,
            order_by: ''
        },
        elm = {
            demandLinks: $('#market_demand a.market_res_select, #demand_type_select a.market_res_select'),
            offerLinks: $('#market_offer a.market_res_select, #offer_type_select a.market_res_select')
        };
    var distance_slider, ratio_slider, demand_slider, offer_slider, amount_slider, amount_wrapper;
    this.bindEvents = function () {
        $('#market_controls').click(handleClick);
        $('#building_market_endless_scroll').click(handleClickOnList);
        if (type === 'create') {
            $('#delete_own_offer').click(deleteOffer);
        }
        $('#check_all').click(checkAll);
        $('#market_controls a.market_res_select').mouseover(handleMouseover);
    };
    this.unbindEvents = function () {
        $('#market_controls').unbind();
        $('#market_controls a.market_res_select').unbind();
        $('#building_market_endless_scroll').unbind();
    };

    function checkAll() {
        $('#market_offers input').attr('checked', this.checked ? 'checked' : '');
    }

    function deleteOffer() {
        var ids = [],
            wnd = GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_MARKET) || GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_BUILDING);
        $("#market_offers input[type='checkbox']").each(function () {
            if ($(this).attr('checked')) {
                ids.push($(this).val());
            }
        });
        wnd.requestContentPost('building_market', 'delete_offer', {
            offer_ids: ids
        });
    }

    function checkSliderValue(res) {
        if (offer_slider) {
            offer_slider.disable(!params.offer_type);
            offer_slider.setMax(Math.min(data.available_capacity, ITowns.getTown(Game.townId).resources()[res]));
        }
        if (demand_slider) {
            demand_slider.disable(!params.demand_type);
            var sliderMax;
            if ($('#offer_type_select a.selected').length > 0) {
                sliderMax = ~~Math.min(offer_slider._value * data.max_trade_ratio, 22500);
            } else {
                sliderMax = 22500;
            }
            demand_slider.setMax(sliderMax);
            demand_slider.setMin(~~ (Math.max(offer_slider._value * (1 / data.max_trade_ratio), 100)));
        }
    }

    function handleMouseover(e) {
        var target = e.target,
            href, tmp, res;
        if (target && (href = target.href)) {
            tmp = href.split(/#/)[1].split(/_/);
            res = tmp[1];
            checkSliderValue(res);
        }
    }

    function handleClick(e) {
        var target = e.target,
            href, wnd = GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_MARKET) || GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_BUILDING),
            tmp, res, reload = false;
        if (target.className === 'middle') {
            target = target.parentNode.parentNode.parentNode;
        }
        if (target && (href = target.href)) {
            tmp = href.split(/#/)[1].split(/_/);
            if (type === 'market') {
                reload = true;
            }
            switch (tmp[0]) {
            case 'create':
                createOwnOffer();
                break;
            case 'offer':
            case 'demand':
                res = tmp[1];
                params[tmp[0] + '_type'] = (params[tmp[0] + '_type'] === res) ? '' : res;
                elm[tmp[0] + 'Links'].removeClass('selected').filter('.' + (params[tmp[0] + '_type'] || 'foo')).addClass('selected');
                checkSliderValue(res);
                $('#market_create_new_offer').toggleClass('inactive', !(params.offer_type && params.demand_type));
                break;
            case 'ratio':
            case 'distance':
                if (params.order_by === tmp[0]) {
                    params.order_by = '';
                } else {
                    params.order_by = tmp[0];
                }
                break;
            case 'set':
                if (tmp[1] === 'ratio' && params.max_ratio) {
                    if (params.order_by === 'ratio') {
                        params.order_by = '';
                    } else {
                        params.max_ratio = ratio_slider.getValue();
                        params.order_by = 'ratio';
                    }
                } else {
                    if (params.order_by === 'distance' && params.max_delivery_time) {
                        params.order_by = '';
                    } else {
                        params.max_delivery_time = distance_slider.getValue();
                        params.order_by = 'distance';
                    }
                }
                break;
            default:
                break;
            }
            if (params.offer_type === undefined) {
                params.offer_type = '';
            }
            if (params.demand_type === undefined) {
                params.demand_type = '';
            }
            highlightActiveSort(params.order_by);
        }
        e.preventDefault();
        if (amount_wrapper) {
            amount_wrapper.hide();
        }
        if (reload || paramsComplete()) {
            wnd.requestContentGet('building_market', 'offer_list', params);
        }
    }

    function paramsComplete() {
        var i, complete = true;
        for (i in params) {
            if (params[i] === 0 || params[1] === '') {
                complete = false;
            }
        }
        return complete;
    }

    function highlightActiveSort(str) {
        $('#market_controls a.market_option').each(function () {
            this.innerHTML = this.innerHTML.split('\u25bc').join('');
            if (str.length && this.href.indexOf(str) !== -1) {
                this.innerHTML += '\u25bc';
            }
        });
    }

    function handleClickOnList(e) {
        var target = e.target,
            href, tmp;
        if (target && (href = target.href)) {
            tmp = href.split(/#/)[1].split(/_/);
            switch (tmp[0]) {
            case 'accept':
                acceptOffer(tmp[1]);
                break;
            case 'showslider':
                var w, off1, off2, data, button_w = 22,
                    button_h = 23;
                data = $.parseJSON(atob(tmp[1]));
                off1 = $(target).offset();
                off2 = $('#building_market_table_wrapper').offset();
                w = amount_wrapper.width();
                amount_wrapper.css({
                    left: off1.left - off2.left - w + button_w,
                    top: off1.top - off2.top + button_h * 2
                }).toggle(last_id !== data.id);
                last_id = (last_id === data.id) ? false : data.id;
                $('#building_market_table_wrapper').bind('scroll.slider', function (e) {
                    amount_wrapper.hide();
                    $(this).unbind('scroll.slider');
                });
                amount_slider.setMax(data.max);
                amount_slider.setValue(data.max);
                break;
            }
        }
        e.preventDefault();
    }

    function acceptOffer(data) {
        var wnd = GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_MARKET) || GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_BUILDING);
        data = $.parseJSON(atob(data));
        params.amount = (last_id == data.id) ? amount_slider.getValue() : data.max;
        params.offer_id = data.id;
        amount_wrapper.hide();
        wnd.requestContentPost('building_market', 'accept_offer', params);
    }

    function createOwnOffer() {
        var wnd = GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_MARKET) || GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_BUILDING);
        params.offer = offer_slider.getValue();
        params.demand = demand_slider.getValue();
        if (!params.max_delivery_time) {
            params.max_delivery_time = distance_slider.getValue();
        }
        wnd.requestContentPost('building_market', 'create_offer', params);
    }

    function initSliders(type) {
        function calcRatio() {
            var ratio = demand_slider.getValue() / offer_slider.getValue();
            ratio = (~~ (ratio * 100)) / 100;
            $('#market_offer_ratio').html(ratio);
        }

        function oChange() {
            $('#market_offer_amount').html(offer_slider.getValue());
            calcRatio();
        }

        function dChange() {
            $('#market_demand_amount').html(demand_slider.getValue());
            calcRatio();
        }
        if (type === 'market') {
            ratio_slider = new Slider({
                elementDown: $('#ratio_down'),
                elementUp: $('#ratio_up'),
                elementInput: $('#input_ratio'),
                elementSlider: $('#ratio_slider'),
                min: 0,
                max: 3,
                step: 0.1
            });
            amount_slider = new Slider({
                elementDown: $('#amount_down'),
                elementUp: $('#amount_up'),
                elementInput: $('#input_amount'),
                elementSlider: $('#trade_amount_slider'),
                min: 100,
                max: 100,
                step: 1
            });
            amount_wrapper = $('#market_trade_slider');
        } else if (type === 'create') {
            $('#market_create_new_offer').addClass('inactive');
            offer_slider = new Slider({
                elementDown: $('#offer_down'),
                elementUp: $('#offer_up'),
                elementInput: $('#input_offer'),
                elementSlider: $('#offer_slider'),
                min: 100,
                max: 100,
                step: 1
            });
            demand_slider = new Slider({
                elementDown: $('#demand_down'),
                elementUp: $('#demand_up'),
                elementInput: $('#input_demand'),
                elementSlider: $('#demand_slider'),
                min: 100,
                max: 100,
                step: 1
            });
            offer_slider.disable(true);
            demand_slider.disable(true);
            offer_slider._elementSlider.bind('change keyup', oChange);
            offer_slider._elementInput.bind('change keyup', oChange);
            demand_slider._elementSlider.bind('change keyup', dChange);
            demand_slider._elementInput.bind('change keyup', dChange);
        }
        distance_slider = new Slider({
            elementDown: $('#time_down'),
            elementUp: $('#time_up'),
            elementInput: $('#input_time'),
            elementSlider: $('#time_slider'),
            min: 0.5,
            max: 48,
            step: 0.5
        });
        if (type === 'create') {
            distance_slider._elementSlider.bind('change keyup', function () {
                $('#market_readable_date').text(readableSeconds(parseFloat(distance_slider.getValue(), 10) * 3600));
            });
        }
    }
    initSliders(type);
    this.bindEvents();
}

var BuildingPlace = {
    celebration_finished_at: {},
    simulator_units: {},
    index_data: {},
    wnd_handle: null,
    not_enough_gold_window: null,
    init: function () {
        GPEvents.global.bind(GPEvents.global.command.send_back_support, function (event, data) {
            var elem = $('#place_units_' + data.support_id);
            if (elem.length > 0) {
                elem.remove();
                if (data.no_units_html !== undefined) {
                    $('ul.game_list').html(data.no_units_html);
                }
            }
        });
    },
    renderIndex: function (wnd) {
        BuildingPlace.wnd_handle = wnd;
        wnd.setContent2(tmpl2(GameData.BuildingPlaceTemplate, BuildingPlace.index_data));
        $('a.place_sendback_all').mousePopup(new MousePopup('<strong>' + 'Return units' + '</strong>'));
        $.each(GameData.units, function (unit) {
            $('div.unit_' + unit).setPopup(unit);
        });
    },
    handleEvents: function (current_tab, wnd) {
        switch (current_tab) {
        case 'index':
            var i, unit_id;
            var units = ITowns.getTown(Game.townId).units();
            BuildingPlace.index_data.all_units = {};
            BuildingPlace.index_data.player_units = {};
            for (unit_id in BuildingPlace.index_data.units) {
                BuildingPlace.index_data.all_units[unit_id] = 0;
                BuildingPlace.index_data.player_units[unit_id] = 0;
            }
            for (i in BuildingPlace.index_data.support_array) {
                var support = BuildingPlace.index_data.support_array[i];
                for (unit_id in support.units) {
                    BuildingPlace.index_data.all_units[unit_id] += support.units[unit_id];
                }
            }
            for (unit_id in units) {
                if (!units.hasOwnProperty(unit_id) || !isNumber(units[unit_id]) || units[unit_id] == 0) {
                    continue;
                }
                BuildingPlace.index_data.player_units[unit_id] += units[unit_id];
                BuildingPlace.index_data.all_units[unit_id] += units[unit_id];
            }
            for (unit_id in BuildingPlace.index_data.units) {
                if (BuildingPlace.index_data.all_units[unit_id] == 0) {
                    delete BuildingPlace.index_data.all_units[unit_id];
                }
                if (BuildingPlace.index_data.player_units[unit_id] == 0) {
                    delete BuildingPlace.index_data.player_units[unit_id];
                }
            }
            BuildingPlace.renderIndex(BuildingPlace.wnd_handle);
            break;
        default:
            break;
        }
    },
    startCountdown: function () {
        var that = this;
        $.each(this.celebration_finished_at, function (type, at) {
            if (at > Timestamp.now()) {
                var current_celebration = $('#countdown_' + type);
                current_celebration.countdown(at, {});
                current_celebration.bind('finish', function () {
                    Layout.ajaxGet('building_place', 'culture', {}, true, function (data) {
                        if (that.isBuildingPlaceWindowOpen()) {
                            Layout.buildingWindow.getWnd().setContent2(data.html);
                        }
                    });
                });
            }
        });
    },
    startCelebration: function (celebration_type) {
        var that = this;
        Layout.ajaxPost('building_place', 'start_celebration', {
            celebration_type: celebration_type
        }, true, function (data) {
            if (typeof data.enough_gold !== 'undefined' && data.enough_gold === false) {
                BuildingPlace.showNotEnoughGoldWindow();
            } else {
                if (that.isBuildingPlaceWindowOpen()) {
                    Layout.buildingWindow.getWnd().setContent2(data.html);
                }
                if (celebration_type == 'games') {
                    Kontagent.trackEvent({
                        userId: Game.uid,
                        eventName: 'celebration',
                        subtype1: Game.market_id,
                        subtype2: Game.world_id,
                        subtype3: 'buy_pa_feature'
                    }, function () {});
                }
            }
        });
    },
    showNotEnoughGoldWindow: function () {
        Layout.ajaxGet('building_place', 'not_enough_gold_template', {}, false, function (data) {
            if (!BuildingPlace.not_enough_gold_window) {
                BuildingPlace.not_enough_gold_window = Layout.dialogWindow.open(data.html, 'Olympic Games', 409, 252, function () {
                    BuildingPlace.not_enough_gold_window = null;
                }, true);
            }
        });
    },
    isBuildingPlaceWindowOpen: function () {
        return Layout.buildingWindow.getWnd() != undefined && Layout.buildingWindow.getWnd().getHandler().currentBuilding == 'place' ? 1 : 0;
    },
    insertUnitsToSimulator: function () {
        Layout.buildingWindow.open('place', false, 'simulator', BuildingPlace.simulator_units);
    },
    sendBackSupport: function (support_id) {
        Layout.ajaxPost('building_place', 'send_back', {
            support_id: support_id
        }, false, function (data) {
            var all_units = data.all_units;
            var player_units = data.player_units;
            var support_units = data.support_array;
            $("#support_units_" + support_id).remove();
            if ($(support_units).length == 0) {
                $("#support_units").remove();
            }
            $.each(GameData.units, function (unit) {
                if ($(support_units).length > 0) {
                    if (all_units[unit] > 0) {
                        $("#all_units_" + unit + " span").text(all_units[unit]);
                    } else {
                        $("#all_units_" + unit).remove();
                    }
                }
                if (player_units[unit] > 0) {
                    $("#player_units" + unit + " span").text(player_units[unit]);
                } else {
                    $("#player_units" + unit).remove();
                }
            });
        });
    },
    sendBackBeyond: function (support_id) {
        Layout.showConfirmDialog('Withdraw all units?', 'Do you want to withdraw all units from this city?', function () {
            BuildingPlace.onConfirmSendBackBeyond(support_id);
        });
    },
    initSendBackTab: function (researches) {
        var p = $('#place_defense');
        this.researches = researches;
        addIsNavalToGameDataUnits();
        $.each(GameData.units, function (unit) {
            p.find("div.unit_" + unit).setPopup(unit);
        });
    },
    selectUnit: function (elm) {
        elm = $(elm);
        var input_container = elm.parent().find('div.place_send_part');
        var outer_units_id = ~~input_container[0].id.match(/\d+/);
        var div = $('#place_send_part_' + outer_units_id);
        if (div.is(':visible')) {
            var unit_id = elm[0].className.match(/unit_(\w+)/)[1];
            var count = ~~elm.find('span').html();
            var prev = ~~$('#place_part_' + outer_units_id + '_' + unit_id).val();
            var new_val = prev === count ? 0 : count;
            $('#place_part_' + outer_units_id + '_' + unit_id).val(new_val);
        }
    },
    toggle: function (units_id, base_id) {
        var div, li, pop, cap, progress, units, that = this;
        base_id = base_id || 'place';
        div = $('#' + base_id + '_send_part_' + units_id);
        li = div.parent();
        units = li.find('input');
        if (!div.is(':visible')) {
            progress = li.find('div.progress');
            pop = progress.find('span.capacity_current');
            cap = progress.find('span.capacity_max');
            recalcCapacity(units, false, this.researches, pop, cap, progress);
            units.unbind().bind('keyup change', function () {
                recalcCapacity(units, false, that.researches, pop, cap, progress);
            });
            li.find('a.place_unit.unit').unbind().bind('click', function () {
                BuildingPlace.selectUnit(this);
                recalcCapacity(units, false, that.researches, pop, cap, progress);
            });
        } else {
            units.unbind();
        }
        div.animate({
            height: 'toggle'
        }, 500, function () {}).parent();
    },
    onConfirmSendBackBeyond: function (support_id) {
        Layout.ajaxPost('building_place', 'send_back', {
            support_id: support_id
        }, false, function (data) {
            GPEvents.global.trigger(GPEvents.global.command.send_back_support, [{
                support_id: support_id,
                no_units_html: data.no_units_html
            }]);
        });
    },
    sendBackPart: function (units_id, town_id, base_id) {
        base_id = base_id || 'place';
        var params = {};
        var inputs = $('#' + base_id + '_send_part_' + units_id + ' :input');
        inputs.each(function () {
            var elm = $(this);
            var name = elm.attr('name');
            if (name) {
                params[name] = parseInt(elm.val() || 0, 10);
            }
            elm.val('');
        });
        params.units_id = units_id;
        params.town_id = town_id;
        BuildingPlace.toggle(units_id, base_id);
        Layout.ajaxPost('units_beyond_info', 'send_back_part', params, false, function (data) {
            if (data.close) {
                $("#" + base_id + "_units_" + units_id).remove();
            } else {
                $.each(data.remaining_units, function (name, count) {
                    $('#' + base_id + '_units_' + data.remaining_units.id + ' div.unit_' + name + ' span').text(count);
                });
            }
        });
    }
};

var BuildingTemple = {
    town_god_id: null,
    elm: {},
    init: function (id) {
        $.each(GameData.units, function (unit) {
            $("#" + unit).setPopup(unit);
            $("#" + unit + '_details').setPopup(unit + '_details');
        });
        $.each(GameData.powers, function (power) {
            $("#" + power).setPopup(power);
            $("#god-" + power).setPopup(power);
        });
        $('div.confirm-changing-god').hide();
        this.elm = {
            'button': $('#temple_button'),
            'big': $('#temple_god_big'),
            'fade': $('#temple_god_fade'),
            'statc': $('#temple_god_static'),
            'fav': $('#temple_favor_bar')
        }
    },
    selectGod: function (god_id) {
        $('#temple_' + this.town_god_id).show();
        if (god_id != this.town_god_id) {
            this.elm.fav.fadeOut();
        } else {
            this.elm.fav.fadeIn();
        }
        $('#temple_god_description div.god_info_box').hide();
        $('#temple_god_description div.god_info_box.' + god_id).fadeIn('normal');
        this.elm.fade.attr('class', god_id).fadeIn(500, function () {
            BuildingTemple.elm.statc.attr('class', god_id);
            BuildingTemple.elm.fade.hide();
        });
        if (god_id == this.town_god_id) {
            $('#temple_button').hide();
        } else {
            this.elm.button.show();
            this.elm.button.unbind('click');
            this.elm.button.one('click', BuildingTemple.confirmChangingGod);
            if (this.elm.button.hasClass('temple_button_pos_2')) this.elm.button.removeClass('temple_button_pos_2');
            this.elm.button.addClass('temple_button_pos_1');
        }
        $('div.confirm-changing-god').hide();
        $('#god_id').val(god_id);
        return false;
    },
    confirmChangingGod: function (event) {
        var god_id = $('#god_id').val();
        $('#temple_god_description div.god_info_box').hide();
        $('div.confirm-changing-god').hide();
        $('#confirm-changing-god-' + god_id).fadeIn('normal');
        if (BuildingTemple.elm.button.hasClass('temple_button_pos_1')) BuildingTemple.elm.button.removeClass('temple_button_pos_1');
        BuildingTemple.elm.button.addClass('temple_button_pos_2');
        BuildingTemple.elm.button.one('click', god_id, BuildingTemple.changeGod);
        return false;
    },
    changeGod: function (event) {
        var god_id = event.data;
        Layout.ajaxPost('building_temple', 'change_god', {
            'god_id': god_id
        }, true, function (data) {
            Kontagent.trackEvent({
                userId: Game.uid,
                eventName: 'change_god_to_' + god_id,
                subtype1: Game.market_id,
                subtype2: Game.world_id,
                subtype3: 'change_god'
            }, function () {});
            $('div.confirm-changing-god').fadeOut();
            BuildingTemple.town_god_id = data.god_id;
            BuildingTemple.selectGod(data.god_id);
            if (Layout.unitBar instanceof LayoutModules.PowerBar) Layout.unitBar.update();
        });
        return false;
    }
}

var ChatHistory = {
    items: new Array(),
    add: function (data) {
        if (this.items.length > 0) {
            for (var i = this.items.length - 1; i >= 0; i--) {
                if (this.items[i].date == data.date && this.items[i].text == data.text) return;
            }
        }
        this.items.push(data);
    },
    get: function () {
        return this.items;
    },
    clear: function () {
        this.items = new Array();
    }
};

var Chat = {
    serviceUrl: '/chat/service/',
    connectionId: '',
    requestId: 0,
    queue: [],
    online: false,
    chatRoomName: '',
    chatClientName: '',
    handle: null,
    init: function (handle) {
        this.handle = handle;
        if (!Game || !Game.alliance_id) return;
        this.chatRoomName = 'alliance_' + Game.alliance_id;
        this.chatClientName = Game.player_name;
        document.numOpenPolls = 0;
        this.connectionId = (function (l) {
            var s = '',
                chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
            while (l--) s += chars.charAt(Math.floor(Math.random() * chars.length));
            return s;
        })(16);
        $(window).bind('beforeunload', function () {
            document.numOpenPolls = 99;
        });
        this.handle.redraw();
    },
    actionPoll: function () {
        this.request({
            'op': 'poll'
        });
    },
    actionStatus: function () {
        this.request({
            'op': 'status'
        });
    },
    actionUnregister: function () {
        this.request({
            'op': 'unregister'
        });
    },
    actionConnect: function () {
        if (!this.isOnline()) {
            this.online = true;
            this.actionStatus();
        }
    },
    actionDisconnect: function () {
        if (this.isOnline()) {
            this.actionUnregister();
            this.online = false;
        }
    },
    actionMessage: function (message) {
        if ('' != message) {
            message = encodeURIComponent(message);
            message = message.replace("'", '%27');
            this.request({
                'op': 'message',
                'to': this.chatRoomName,
                'message': message
            });
        }
    },
    actionClearHistory: function () {
        this.request({
            'op': 'clear'
        });
    },
    isOnline: function () {
        return this.online;
    },
    request: function (operation) {
        if (!this.isOnline() || !operation || undefined == operation.op) {
            return;
        }
        var isStatus = ("status" == operation.op);
        var isPoll = ("poll" == operation.op);
        if (isPoll) {
            if (1 <= document.numOpenPolls) return;
            else document.numOpenPolls++;
        }
        var isMessage = ("message" == operation.op);
        if (isMessage) {
            var message = operation.message;
            if (1000 < message.length) message = message.substring(0, 1000) + "...";
            operation = {
                'op': operation.op,
                'to': operation.to
            }
        }
        var params = $.param($.extend(operation, {
            'cid': this.connectionId,
            'rid': ++this.requestId
        })),
            url = this.serviceUrl + '?' + params;
        $.ajax({
            'type': 'POST',
            'url': url,
            'data': isMessage ? "message=" + message : '',
            'success': function (data, textStatus, XMLHttpRequest) {
                if (isStatus || isPoll) {
                    if (isPoll) document.numOpenPolls--;
                    if (0 >= document.numOpenPolls) {
                        window.setTimeout(this.actionPoll.bind(this), 100);
                    }
                }
                this.handleResponse(data);
            }.bind(this),
            'error': function (XMLHttpRequest, textStatus, errorThrown) {
                if (isPoll) document.numOpenPolls--;
                this.handleResponseError($.trim(XMLHttpRequest.responseText));
            }.bind(this)
        });
    },
    getTimestamp: function () {
        return new Date().getTime();
    },
    handleResponse: function (msg) {
        var data = eval("[" + msg + "]")[0];
        if (undefined == data) {
            return;
        }
        if ('poll' === data.action) {
            for (var i = 0; i < data.queue.length; i++) {
                this.handleMessage(data.queue[i]);
                this.handle.notify();
            }
        } else if ('status' === data.action) {
            ChatRoomInfo.set(data.room);
            for (var i = 0; i < data.messages.length; i++) {
                this.handleMessage(data.messages[i]);
            }
            this.handle.redraw();
        }
    },
    handleMessage: function (m) {
        if (!m || 'undefined' == m.type) return;
        m.content = m.content.replace(/&apos;/g, "'");
        switch (m.type) {
        case 'User':
            this.addTextLine({
                'from': m.from,
                'text': m.content,
                'date': m.at
            });
            break;
        case 'Registered':
            ChatRoomInfo.addClient(m.from);
            this.addTextLine({
                'text': "%s has entered the chat room".replace("%s", m.from),
                'date': m.at,
                'isInfo': true
            });
            break;
        case 'Unregistered':
            ChatRoomInfo.removeClient(m.from);
            this.addTextLine({
                'text': "%s has left the chat room".replace("%s", m.from),
                'date': m.at,
                'isInfo': true
            });
            break;
        case 'Kicked':
            this.addTextLine({
                'text': "You have been banned from the alliance",
                'date': m.at,
                'isInfo': true
            });
            this.actionDisconnect();
            break;
        }
    },
    handleResponseError: function (msg) {
        switch (msg) {
        case 'player not found':
            this.addTextLine({
                'text': 'You can\'t use the chat function because you are not a member of any alliance.',
                'isInfo': true
            });
            break;
        case 'not allowed':
            this.addTextLine({
                'text': 'You are not authorized to use the chat function!',
                'isInfo': true
            });
            break;
        case 'invalid request':
            this.addTextLine({
                'text': 'General error',
                'isInfo': true
            });
            break;
        case 'too many connections at once':
            this.addTextLine({
                'text': 'Error - You have to many open connections to the chat. Please close some browser-tabs and join the chat again.',
                'isInfo': true
            });
            break;
        case 'servererror':
        case 'null':
            this.addTextLine({
                'text': 'Server error - please try again in a few minutes.',
                'isInfo': true
            });
            break;
        }
    },
    addTextLine: function (data, addToHistory) {
        if (!data || 'undefined' == data.text) return;
        if (data.from === this.chatClientName) data.from = 'Me';
        if (false != addToHistory) ChatHistory.add(data);
        this.handle.redraw();
    }
};

var ChatRoomInfo = {
    clients: new Array(),
    set: function (data) {
        this.clients = data.clients;
    },
    addClient: function (client) {
        for (var i = 0; i < this.clients.length; i++) {
            if (client == this.clients[i]) return;
        }
        this.clients.push(client);
    },
    removeClient: function (client) {
        var clientsNew = new Array();
        for (var i = 0; i < this.clients.length; i++) {
            if (client == this.clients[i]) continue;
            clientsNew.push(this.clients[i]);
        }
        this.clients = clientsNew;
    },
    clear: function () {
        this.clients = new Array();
    },
    getClients: function () {
        return this.clients;
    }
};

function WndHandlerChat(wndhandle) {
    this.wnd = wndhandle;
    this.elements = {};
    var that = this,
        old_inp_height;
    this.loadElements = function () {
        this.elements = {
            chatPanel: $('#chat_history'),
            chatPanelBg: $('#chat_history_bg'),
            roomInfoPanel: $('#chat_roominfo'),
            textInput: $('#chat_message_text').val(''),
            resize: $('#chat_control_resize'),
            clear: $('#chat_control_clear'),
            textInputClone: $('<div id="chat_message_text_clone"></div>'),
            chatWrapper: $('<div id="chat_wrapper"></div>'),
            menuLink: $('#chat_link'),
            notify: $('<div id="chat_notify"></div>')
        };
        this.elements.textInputClone.css(getInputStyle()).appendTo('body');
        old_inp_height = this.elements.textInput.height();
        bindListeners();
    };

    function getInputStyle() {
        var style_list = ['width', 'lineHeight', 'fontSize', 'letterSpacing', 'fontFamily', 'fontWeight', 'textAlign', 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft'],
            style_copy = {},
            i = style_list.length,
            j, inp = that.elements.textInput;
        while (i--) {
            style_copy[j = style_list[i]] = inp.css(j);
        }
        if (parseInt(style_copy.width, 10) === 0) {
            delete style_copy.width;
        }
        return style_copy;
    }

    function clearHistory() {
        ChatHistory.clear();
        Chat.actionClearHistory();
        that.redraw();
        that.elements.textInput.focus();
    }
    this.renderTextLine = function (data) {
        data.date = data.date || Chat.getTimestamp();
        data.from = data.from || '';
        data.isInfo = data.isInfo || false;
        var html = '',
            date = readableUnixTimestamp(data.date, 'player_timezone');
        from = data.from + (!data.isInfo ? ':' : '');
        text = data.text.strip().replace(/\n/g, "<br/>");
        html += '<li class="chat_line' + (data.isInfo ? ' chat_line_notification' : '') + '">';
        html += '<span class="chat_line_date">' + date + ' </span>';
        html += '<span class="chat_line_sender">' + from + '</span> ';
        html += '<span class="chat_line_message">' + text + '</span>';
        html += '</li>';
        return html;
    };

    function bindListeners() {
        that.elements.clear.click(function () {
            clearHistory();
        });
        that.elements.menuLink.click(function () {
            that.wnd.close();
        });
        that.elements.textInput.bind('keyup.resize', function (ev) {
            var inpClone = that.elements.textInputClone,
                inp = that.elements.textInput,
                panel = that.elements.chatPanelBg,
                new_inner_height, new_outer_height, rescroll;
            if (ev.keyCode === 13) {
                if (ev.altKey || ev.ctrlKey) {
                    this.value += '\n';
                } else {
                    sendMsg();
                    inp.height(old_inp_height);
                    panel.css('bottom', inp.outerHeight());
                }
            }
            inpClone[0].innerHTML = '&nbsp;' + inp[0].value.replace(/\n/g, '<br />');
            if (inp.height() !== inpClone.height()) {
                new_inner_height = Math.max(inpClone.height(), old_inp_height);
                new_outer_height = Math.max(inpClone.outerHeight(), old_inp_height);
                panel = that.elements.chatPanelBg;
                rescroll = panel[0].scrollTop === panel[0].scrollHeight - panel.height();
                inp.height(new_inner_height);
                panel.css('bottom', new_outer_height);
                if (rescroll) {
                    that.rescroll();
                }
            }
            ev.preventDefault();
        });
    }

    function sendMsg() {
        var inp = that.elements.textInput,
            message = $.trim(inp.val());
        if (null != message.match(/\S+/g)) {
            Chat.actionMessage(message);
            inp.val('');
        }
    }
    this.rescroll = function () {
        var panel = this.elements.chatPanelBg[0];
        panel.scrollTop = panel.scrollHeight;
    };
}
WndHandlerChat.inherits(WndHandlerDefault);
WndHandlerChat.prototype.getDefaultWindowOptions = function () {
    var ret = {
        position: ['center', 'center'],
        height: 400,
        width: 500,
        resizable: true,
        'fullwindow': true,
        minimizable: true,
        title: 'Untitled Window'
    };
    return ret;
};
WndHandlerChat.prototype.onInit = function (title, UIopts) {
    this.wnd.requestContentGet('chat', 'init', {});
    return true;
};
WndHandlerChat.prototype.onRcvData = function (data) {
    this.wnd.setContent2(data.html);
    this.loadElements();
    Chat.init(this);
    Chat.actionConnect();
    this.elements.clear.mousePopup(new MousePopup('Delete history'));
};
WndHandlerChat.prototype.redraw = function () {
    var elem = this.elements;
    var items = ChatHistory.get(),
        clients = ChatRoomInfo.getClients(),
        html = '',
        panel = elem.chatPanelBg,
        rescroll = panel[0].scrollTop === panel[0].scrollHeight - panel.height(),
        i, l = items.length;
    for (i = 0; i < l; i++) {
        html += this.renderTextLine(items[i]);
    }
    elem.chatPanel.html('');
    elem.chatPanel.append(html);
    if (rescroll) {
        this.rescroll();
    }
    html = '<ul class="game_list">';
    for (i = 0; i < clients.length; i++) {
        html += '<li class="chat_roominfo_client ' + (i % 2 ? 'odd' : 'even') + '" title="' + clients[i] + '"><span class="player_icon">' + clients[i] + '</span></li>';
    }
    html += '</ul>';
    elem.roomInfoPanel.html(html);
    return false;
};
WndHandlerChat.prototype.notify = function () {
    var that = this;
    if (!this.wnd.isMinimized) {
        window.clearTimeout(this.notify_timeout);
    }
    this.elements.notify.appendTo(this.wnd.getMinimized()).animate({
        top: -10
    }, 600, 'bounce', function () {
        that.notify_timeout = window.setTimeout(function () {
            that.elements.notify.animate({
                top: -3
            });
            that.notify();
        }, 1500);
    });
    return false;
};
WndHandlerChat.prototype.onClose = function () {
    var i, elem = this.elements;
    for (i in elem) {
        elem[i].unbind();
    }
    elem.menuLink.click(function () {
        Layout.wnd.Create(Layout.wnd.TYPE_CHAT, 'Chat', {});
    });
    elem.textInputClone.remove();
    delete elem.textInputClone;
    Chat.actionDisconnect();
    return true;
};

function DefaultColorPicker(root) {
    var jqroot, that = this,
        wnd, picker, elm = {},
        args = arguments;

    function clearSelection() {
        if (that.previous) {
            var prev = that.previous.style;
            prev.border = '';
            prev.width = '';
            prev.height = '';
            prev.borderTop = '1px solid #000';
            prev.borderLeft = '1px solid #000';
        }
    }

    function setColors(col) {
        var x = 0,
            y = 0;
        elm.tones.children('div').each(function () {
            var xx = x++ % 6,
                yy = xx == 5 ? y++ : y,
                light;
            xx /= 6;
            yy /= 6;
            with(Math) {
                xx = sqrt(xx);
                light = max(0, 255 - 127 * yy);
                var r = floor(max(0, min(255, (col[0] * yy - 255 * (xx)) + light)));
                var g = floor(max(0, min(255, (col[1] * yy - 255 * (xx)) + light)));
                var b = floor(max(0, min(255, (col[2] * yy - 255 * (xx)) + light)));
            }
            this.style.backgroundColor = 'rgb(' + r + ',' + g + ',' + b + ')';
        });
    }

    function set(color) {
        var inp = picker.find('input.bb_color_picker_tx')[0];
        var rr = color[0].toString(16),
            gg = color[1].toString(16),
            bb = color[2].toString(16);
        rr = rr.length < 2 ? '0' + rr : rr;
        gg = gg.length < 2 ? '0' + gg : gg;
        bb = bb.length < 2 ? '0' + bb : bb;
        inp.value = (rr + gg + bb).toUpperCase();
    }
    this.create = function (root) {
        create(root);
    };

    function rebind() {
        $.each(elm, function () {
            this.unbind();
        });
        jqroot.unbind().click(function () {
            that.constructor.apply(that, args);
        });
    }
    this.remove = function () {
        this.close();
    };

    function create(root) {
        jqroot = $(root);
        wnd = w(root).getJQElement();
        picker = $('#bbcode_popups').find('div.bb_color_picker').clone();
        elm = {
            'confirm': picker.find('a.confirm'),
            'remove': picker.find('a.cancel'),
            'inp': picker.find('input'),
            'tones': picker.find('div.bb_color_picker_tones'),
            'colors': picker.find('div.bb_color_picker_colors').children(),
            'preview': picker.find('div.bb_color_picker_preview')
        };
        jqroot.unbind().click(function () {
            that.close();
        });
        elm.confirm.click(function () {
            that.confirm.call(that, elm);
        }).mousePopup(new MousePopup('Save color'));
        elm.remove.click(function () {
            that.remove.call(that, elm);
        }).mousePopup(new MousePopup('Delete color'));
        root.className += ' picker_active';
        picker.bind('keydown', function (event) {
            if (event.keyCode == 27) that.close();
            return false;
        }).detach().appendTo(w(root).getJQElement()).css(jqroot.position()).show();
        elm.inp.focus();
        elm.colors.each(function () {
            $(this).click(function () {
                var bg_color = this.style.backgroundColor;
                var rgb = [0, 0, 0];
                if (bg_color.length == 4 && bg_color.substr(0, 1) == '#') {
                    bg_color = '#' + bg_color.substr(1, 1) + bg_color.substr(1, 1) + bg_color.substr(2, 1) + bg_color.substr(2, 1) + bg_color.substr(3, 1) + bg_color.substr(3, 1);
                    rgb[0] = parseInt(bg_color.substr(1, 2), 16);
                    rgb[1] = parseInt(bg_color.substr(3, 2), 16);
                    rgb[2] = parseInt(bg_color.substr(5, 2), 16);
                } else if (bg_color.substr(0, 1) == '#') {
                    rgb[0] = parseInt(bg_color.substr(1, 2), 16);
                    rgb[1] = parseInt(bg_color.substr(3, 2), 16);
                    rgb[2] = parseInt(bg_color.substr(5, 2), 16);
                } else {
                    rgb = bg_color.match(/\d+/g);
                    rgb = [~~rgb[0], ~~rgb[1], ~~rgb[2]];
                }
                setColors(rgb);
                clearSelection();
            })
        });
        elm.tones.click(function (ev) {
            clearSelection();
            if (ev.target.style.backgroundColor == '') return;
            that.previous = ev.target;
            that.saved_style = {
                border: ev.target.style.border,
                width: ev.target.style.width,
                height: ev.target.style.height
            };
            ev.target.style.border = '2px solid #fff'
            ev.target.style.width = '13px';
            ev.target.style.height = '13px';
            elm.preview[0].style.color = (ev.target.style.backgroundColor);
            var str = ev.target.style.backgroundColor,
                hex = str.charAt(0) === '#',
                rgb = hex ? str.match(/\w{2}/g) : str.match(/\d+/g),
                radix = (hex ? 16 : 10),
                i = rgb.length;
            while (i--)
            rgb[i] = parseInt(rgb[i], radix);
            set(rgb);
        });
        setColors([255, 0, 0]);
    }
    this.close = function () {
        clearSelection();
        rebind();
        root.className.replace(' picker_active', '');
        elm.colors.each(function () {
            $(this).unbind()
        });
        elm.tones.unbind();
        picker.unbind().remove();
        return false;
    };
    this.create(root);
}

function ColorPicker(root, id, type) {
    ColorPicker.prototype.constructor = ColorPicker;
    DefaultColorPicker.apply(this, arguments);
    var current_town_player_id = id,
        that = this;
    this.confirm = function (elm) {
        switch (type) {
        case 'player':
            changePlayerColor(id, elm.inp.val());
            break;
        case 'alliance':
            changeAllianceColor(id, elm.inp.val());
        }
        this.close();
    };
    this.remove = function () {
        removeCurrentAssignment();
        this.close();
    };

    function changeAllianceColor(id, color) {
        Layout.ajaxPost('alliance', 'assign_map_color', {
            alliance_id: id,
            color: color,
            current_town_player_id: Game.player_id
        }, true, function (data) {
            that.type = null;
            that.other_id = 0;
            WMap.mapData.updateColors(data.town_ids, data.color);
            changeColorsOnMap(data);
        });
    }

    function changePlayerColor(id, color) {
        Layout.ajaxPost('player', 'assign_map_color', {
            player_id: id,
            color: color
        }, true, function (data) {
            that.type = null;
            that.other_id = 0;
            WMap.mapData.updateColors(data.town_ids, data.color);
            changeColorsOnMap(data);
        });
    }

    function removeCurrentAssignment() {
        switch (type) {
        case 'alliance':
            Layout.ajaxPost('alliance', 'remove_map_color_assignment', {
                alliance_id: id,
                current_town_player_id: current_town_player_id
            }, true, function (data) {
                WMap.mapData.updateColors(data.town_ids, data.color);
                changeColorsOnMap(data);
            });
            break;
        case 'player':
            Layout.ajaxPost('player', 'remove_map_color_assignment', {
                player_id: id
            }, true, function (data) {
                WMap.mapData.updateColors(data.town_ids, data.color);
                changeColorsOnMap(data);
            });
            break;
        }
    }

    function changeColorsOnMap(data) {
        if (!(ColorPicker.type == 'alliance' && data.has_custom_player_color)) {
            w(root).getJQElement().find('.actual_flag').attr('style', 'background-color: #' + data.color + ';');
        }
        Picomap.refresh();
        $.each(data.town_ids, function (id, town_id) {
            var elem = $('#town_flag_' + town_id);
            if (typeof elem != 'undefined') {
                elem.css({
                    backgroundColor: '#' + data.color
                });
            }
        });
    }
}
ColorPicker.prototype = DefaultColorPicker;

function BBCodeColorPicker(root, callback) {
    BBCodeColorPicker.prototype.constructor = BBCodeColorPicker;
    DefaultColorPicker.apply(this, arguments);
    this.confirm = function (elm) {
        callback(elm.inp.val());
        this.close();
    };
}
BBCodeColorPicker.prototype = DefaultColorPicker;

var Conquest = {
    conquest_finished_at: null,
    startCountdown: function () {
        if (this.conquest_finished_at > Timestamp.server()) {
            var conquest = $('#conquest');
            conquest.countdown(this.conquest_finished_at, {});
            conquest.bind('finish', function () {
                window.location.href = url('index');
            });
        }
    }
}

function Espionage(target_id, wnd_handle, hide_storage) {
    this.slider = null;
    this.target_id = target_id;
    this.root = wnd_handle.getJQElement();
    this.hide_storage = hide_storage;
    $(document).unbind('notification.dataChangedHide', this.notificationEventHandler);
    $(document).bind('notification.dataChangedHide', this.notificationEventHandler);
    this.init();
}
Espionage.prototype.init = function () {
    var min, max;
    var that = this;
    if (this.root.find('div.espionage_order_box').length) {
        min = 1000;
        max = this.hide_storage;
        this.initSlider();
        this.slider.setMin(min);
        this.slider.setMax(max);
        this.slider.setValue(min);
    }
    this.root.find('div.espionage_spy_button').bind('click', function () {
        that.spy();
    });
    this.root.find('a.espionage_goto_hide').bind('click', this.gotoHide);
};
Espionage.prototype.cleanUp = function () {
    this.root.find('div.espionage_spy_button').unbind('click');
    this.root.find('a.espionage_goto_hide').unbind('click');
    this.root.find('div.espionage_order_slider').unbind('change');
};
Espionage.prototype.notificationEventHandler = function (evt, data) {
    var params = $.parseJSON(data.param_str);
    if (params.espionage_storage) {
        if (params.storage_1k_border == 1) {
            GPEvents.global.trigger(GPEvents.global.town.hide_has_changed, params);
        } else {
            $('span.espionage_stored_iron').each(function () {
                $(this).html(params.espionage_storage);
            });
        }
    }
};
Espionage.prototype.initSlider = function () {
    var element_slider = this.root.find('div.espionage_order_slider');
    this.slider = new Slider({
        elementMin: this.root.find('a.espionage_order_min'),
        elementMax: this.root.find('a.espionage_order_max'),
        elementDown: this.root.find('a.espionage_order_down'),
        elementUp: this.root.find('a.espionage_order_up'),
        elementInput: this.root.find('input.espionage_order_input'),
        elementSlider: element_slider
    });
    element_slider.bind('change', this.changeCount);
};
Espionage.prototype.changeCount = function () {
    if (this.root) {
        this.root.find('input.espionage_order_input').val(this.slider.getValue());
    }
};
Espionage.prototype.spy = function () {
    var that = this;
    if (this.root) {
        Layout.ajaxPost('town_info', 'spy', {
            id: this.target_id,
            espionage_iron: this.root.find('input.espionage_order_input').val()
        }, true, function (data) {
            that.root.find('div.espionage_espionage').parent().html($(data.html));
            that.init();
        });
    }
};
Espionage.prototype.gotoHide = function () {
    Layout.buildingWindow.open('hide');
};

var FightSimulator = {
    units: {},
    gods: {},
    att_god_id: {},
    def_god_id: {},
    fight_result: {},
    initialize: function (options) {
        this.units = options.units || [];
        this.gods = options.gods || [];
        this.att_god_id = {};
        this.def_god_id = {};
        this.refreshForm();
        $.each(GameData.units, function (unit) {
            $("#building_place_" + unit).setPopup(unit + '_details');
        });
        var img_popups = $('table.place_simulator_table img[alt]');
        img_popups.each(function (i, elm) {
            elm = $(elm);
            if (elm.attr('alt').length) {
                elm.mousePopup(new MousePopup(elm.attr('alt')));
            }
        });
        $('#insert_survives_def_units').mousePopup(new MousePopup('Add the defender\'s remaining units again'));
        var that = this;
        $('#simulator_mods_morale').unbind('click').bind('click', function () {
            that.openMoral();
        });
        $('#simulator_body').find('.unit').click(function () {
            var unit_id = $(this).attr('id').replace('building_place_', '');
            var unit_max = ~~$(this).find('span').text();
            var input_field = $('input[name="sim[units][att][' + unit_id + ']"]');
            if (input_field.val() == unit_max) {
                input_field.val(0);
            } else {
                input_field.val(unit_max);
            }
        });
    },
    simulate: function () {
        var params = $('#place_simulator_form').serialize();
        var unit_id, count;
        Layout.ajaxGet('building_place', 'simulate', {
            'simulator': params
        }, false, function (data) {
            for (unit_id in data.att_losses) {
                count = data.att_losses[unit_id];
                $('#building_place_att_losses_' + unit_id).text(count ? count : '');
            }
            for (unit_id in data.def_losses) {
                count = data.def_losses[unit_id];
                $('#building_place_def_losses_' + unit_id).text(count ? count : '');
            }
            $('#building_place_def_losses_wall_level').text(data.wall_loss);
            this.fight_result = data;
        }.bind(this));
        return false;
    },
    switchGod: function (player, god_id) {
        this[player + '_god_id'] = god_id;
        this.refreshForm();
    },
    refreshForm: function () {
        var god;
        for (god in this.gods) {
            $('td.building_place_' + god).hide();
            $('input.building_place_att_' + god).hide();
            $('input.building_place_def_' + god).hide();
            $('input.building_place_att_' + god + '[type="text"]').val('');
            $('input.building_place_def_' + god + '[type="text"]').val('');
            $('input.building_place_att_' + god + '[type="checkbox"]').attr('checked', false);
            $('input.building_place_def_' + god + '[type="checkbox"]').attr('checked', false);
        }
        $('td.building_place_' + this.att_god_id).show();
        $('td.building_place_' + this.def_god_id).show();
        $('input.building_place_att_' + this.att_god_id).show();
        $('input.building_place_def_' + this.def_god_id).show();
        $('div.place_sim_wrap_mods_extended input[type="checkbox"]').each(function (i, elm) {
            if ($(elm).attr('name').match(/power_/g)) {
                $(elm).attr('checked', false);
            }
        });
    },
    openModsExtended: function () {
        $('div.place_sim_wrap_mods_extended').fadeIn(100);
        return false;
    },
    closeModsExtended: function () {
        var params = $('#place_simulator_form').serialize();
        var i;
        Layout.ajaxGet('building_place', 'simulate_bonuses', {
            'simulator': params
        }, false, function (data) {
            for (i in data) {
                $('td.building_place_' + i + ' span.percentage').text(data[i] + '%');
            }
            $('div.place_sim_wrap_mods_extended').fadeOut(100);
        }.bind(this));
        return false;
    },
    openMoral: function () {
        $('div.place_sim_wrap_mods_extended').fadeOut(100);
        $('div.place_sim_wrap_mods_moral').fadeIn(100);
        return false;
    },
    closeMoral: function () {
        var defender_name = $('#morale_player_name').val();
        if (defender_name.length > 0) {
            Layout.ajaxGet('building_place', 'simulate_moral', {
                'defender_name': defender_name
            }, false, function (data) {
                $("input[name='sim[mods][att][morale]']").val(data.morale);
                $('div.place_sim_wrap_mods_moral').fadeOut(100);
            }.bind(this));
        } else {
            $("input[name='sim[mods][att][morale]']").val('');
            $('div.place_sim_wrap_mods_moral').fadeOut(100);
        }
        return false;
    },
    insertUnits: function (units, type) {
        $.each(units, function (unit_id, count) {
            var field_name = s("sim[units][%1][%2]", type, unit_id);
            $("input[name='" + field_name + "']").val(count);
        });
    },
    insertHomeUnits: function () {
        var type = $('#select_insert_units').val();
        Layout.ajaxGet('building_place', 'get_home_units', {
            type: type
        }, false, function (data) {
            FightSimulator.insertUnits(data.units, type);
        });
    },
    insertSurvivesDefUnitsAsNewDefender: function () {
        if (!this.fight_result.def_survives) {
            HumanMessage.error('No battle has taken place yet');
            return false;
        }
        this.insertUnits(this.fight_result.def_survives, 'def')
        $('td.place_losses').empty();
        this.simulate();
        return false;
    }
}

var Forum = {
    forum_datas: {},
    flag_separate_forum_tab: false,
    forum_id: 0,
    getThreadsPerPage: function () {
        return this.threads_per_page;
    },
    setThreadsPerPage: function (threads_per_page) {
        if (this.flag_separate_forum_tab) {
            this.threads_per_page = threads_per_page;
        }
    },
    checkAllThreads: function (ele) {
        $("#thread_list_form INPUT[type='checkbox'][name!='ignore']").attr('checked', $(ele).attr('checked'));
        $('div.threadicon').toggleClass('checked', $(ele).attr('checked'));
    },
    checkThread: function (ele) {
        var checkbox = $(ele).children("INPUT[type='checkbox']");
        var checked = checkbox.attr('checked');
        checkbox.attr('checked', !checked);
        checked = !checked;
        $(ele).children('div.threadicon').toggleClass('checked', checked);
    },
    deleteThreads: function () {
        if (confirm('Really delete topics?')) {
            var params = {};
            params.action = 'threads_delete';
            Forum.sendCommandForMultipleThreads(params);
        }
    },
    closeOpenThreads: function () {
        var params = {};
        params.action = 'threads_close_open';
        Forum.sendCommandForMultipleThreads(params);
    },
    moveThreads: function () {
        var params = {};
        var forum_id = $('#admin_functions select').val();
        params.action = 'threads_move';
        params.forum_id = forum_id;
        Forum.sendCommandForMultipleThreads(params);
    },
    sendCommandForMultipleThreads: function (params) {
        params.thread_ids = [];
        $('#threadlist input:checkbox:checked').each(function (i, elem) {
            params.thread_ids[i] = $(elem).val();
        });
        Layout.allianceForum.getWnd().clearMenu();
        Layout.allianceForum.getWnd().requestContentPost('alliance_forum', 'forum', params);
    },
    deleteThread: function (thread_id) {
        if (confirm('Do you really want to delete this topic?')) {
            Layout.allianceForum.getWnd().clearMenu();
            Layout.allianceForum.getWnd().requestContentPost('alliance_forum', 'forum', {
                'action': 'thread_delete',
                'thread_id': thread_id
            });
        }
    },
    closeThread: function (thread_id) {
        Layout.allianceForum.getWnd().clearMenu();
        Layout.allianceForum.getWnd().requestContentPost('alliance_forum', 'forum', {
            'action': 'thread_close',
            'thread_id': thread_id
        });
    },
    openThread: function (thread_id) {
        Layout.allianceForum.getWnd().clearMenu();
        Layout.allianceForum.getWnd().requestContentPost('alliance_forum', 'forum', {
            'action': 'thread_open',
            'thread_id': thread_id
        });
    },
    viewThread: function (thread_id, goto_last_post, page) {
        var params = {
            'thread_id': thread_id,
            'page': page || 1
        };
        if (goto_last_post) {
            params.action = 'lastpost';
            Layout.allianceForum.getWnd().sendMessage('go', params, this.scrollPostsDown);
        } else {
            Layout.allianceForum.getWnd().sendMessage('go', params);
        }
    },
    editThread: function () {
        $('#forum_thread_name_span_text_admin').css('display', 'none');
        $('#forum_thread_title').css('display', 'none');
        $('#forum_thread_name_span_input').css('display', '');
    },
    updateThread: function () {
        var thread_id = $('#forum_thread_id_input').attr('value');
        var thread_name = $('#forum_thread_name_input').attr('value');
        var thread_important = $('#forum_thread_important_input').attr('checked');
        Layout.allianceForum.getWnd().ajaxRequestPost('alliance_forum', 'forum', {
            'action': 'thread_update',
            'thread_id': thread_id,
            'thread_name': thread_name,
            'thread_important': thread_important
        }, function (_wnd, data) {
            if (data.success) {
                $('#forum_thread_name_span_text_admin').css('display', '');
                $('#forum_thread_title').css('display', '');
                $('#forum_thread_name_span_input').css('display', 'none');
                var html = thread_important ? ('Important:' + ' ') : '';
                html += '<span class="title">' + thread_name + '</span>';
                $('#forum_thread_name_span_text_admin').html(html);
            }
        });
    },
    editForum: function (index, elm) {
        if (!Forum.tboxes) {
            Forum.tboxes = $('#forumlist li.forum div.name form div.text_box');
        }
        Forum.tboxes.each(function () {
            var that = $(this);
            if (that.hasClass('expanded')) {
                that.stop(true, true).css({
                    right: 0
                }).show().removeClass('expanded');
                that.next().stop(true, true).hide();
            }
        });
        var tbox = $(elm),
            ebox = tbox.next();
        tbox.addClass('expanded').stop(true, true).animate({
            right: tbox.width()
        }, 300, 'linear', function () {
            $(this).hide();
            ebox.stop(true, true).slideDown(300);
        });
    },
    shareForum: function (elm, forum_id) {
        Forum.forum_id = forum_id;
        var html = $('#share_forum_with_alliance'),
            list = $(elm).parents('ul.shared_with_list').clone().attr('class', ''),
            label_box = $('<div class="label_box"></div>'),
            input_box = $('<div class="input_box" id="remove_alliances" style="position:relative;"></div>'),
            list_2 = $('#shared_with_list').empty(),
            elm_count = 0;
        list.children().each(function () {
            var that = $(this).children('a').first();
            var id = $(this).attr('class').match(/alliance_id_(\d+)/);
            if (id) {
                var l = $('<label></label>');
                that.appendTo(l);
                label_box.append(l);
                elm_count++;
                input_box.append('<span><input type="checkbox" name="ally" value="' + id[1] + '"/></span>');
            } else {
                that.remove();
            }
        });
        if (elm_count) {
            list_2.prev().show();
            input_box.append('<a href="#" id="remove_share_confirm" class="confirm" onclick="Forum.removeAllianceFromSharedForum(' + forum_id + ');" style="position:absolute; right: 59px;bottom: 0;"></a>');
            label_box.appendTo(list_2.empty());
            input_box.appendTo(list_2);
        } else {
            list_2.prev().hide();
        }
        jQuery.blocker({
            caching: true,
            html: html,
            height: 300,
            width: 400,
            title: 'Forum',
            success: $('#share_forum_confirm, #remove_share_confirm')
        });
    },
    addAllianceToSharedForum: function () {
        var alliance_name = $('#share_forum_with_alliance #shared_with_name').val();
        $('#share_forum_with_alliance').hide();
        Layout.allianceForum.getWnd().clearMenu();
        Layout.allianceForum.getWnd().requestContentPost('alliance_forum', 'forum', {
            'action': 'add_alliance',
            'forum_id': Forum.forum_id,
            'alliance_name': alliance_name
        });
    },
    removeAllianceFromSharedForum: function (forum_id) {
        var alliance_ids = $('#remove_alliances').find('input:checked');
        alliance_ids.each(function () {
            Layout.allianceForum.getWnd().clearMenu();
            Layout.allianceForum.getWnd().requestContentPost('alliance_forum', 'forum', {
                'action': 'remove_alliance',
                'forum_id': forum_id,
                'alliance_id': $(this).val()
            });
        });
    },
    delSharedForum: function (forum_id) {
        Layout.allianceForum.getWnd().clearMenu();
        Layout.allianceForum.getWnd().requestContentPost('alliance_forum', 'forum', {
            'action': 'del_shared_forum',
            'forum_id': forum_id
        });
    },
    addSharedForum: function (forum_id) {
        Layout.allianceForum.getWnd().clearMenu();
        Layout.allianceForum.getWnd().requestContentPost('alliance_forum', 'forum', {
            'action': 'add_shared_forum',
            'forum_id': forum_id
        });
    },
    ignoreForum: function (forum_id) {
        var ignore = $('#ignore_forum').attr('checked');
        Layout.allianceForum.getWnd().clearMenu();
        Layout.allianceForum.getWnd().requestContentPost('alliance_forum', 'forum', {
            'action': 'forum_ignore',
            'forum_id': forum_id,
            'ignore': ignore
        });
    },
    deletePost: function (thread_id, post_id, confirmed) {
        if (!confirmed) {
            jQuery.blocker({
                caching: false,
                width: 250,
                height: 150,
                title: 'Forum',
                html: $('#delete_post_dialog'),
                success: '#delete_post_confirm',
                onSuccess: function () {
                    Forum.deletePost(thread_id, post_id, true);
                },
                cancel: '#delete_post_cancel'
            });
        } else {
            Layout.allianceForum.getWnd().clearMenu();
            Layout.allianceForum.getWnd().requestContentPost('alliance_forum', 'forum', {
                'action': 'post_delete',
                'thread_id': thread_id,
                'post_id': post_id
            });
        }
    },
    openPlayerProfile: function (player_name, player_id) {
        Layout.playerProfile.open(player_name, player_id);
    },
    openAllianceProfile: function (alliance_name, alliance_id) {
        Layout.allianceProfile.open(alliance_name, alliance_id);
    },
    newThread: function (forum_id) {
        Layout.allianceForum.getWnd().sendMessage('go', {
            action: 'thread_new',
            forum_id: forum_id
        });
    },
    newPoll: function (forum_id) {
        Layout.allianceForum.getWnd().sendMessage('go', {
            action: 'poll_new',
            forum_id: forum_id
        });
    },
    switchForum: function (forum_id, page) {
        page = page || 1;
        var w = Layout.allianceForum.getWnd();
        w.sendMessage('go', {
            forum_id: forum_id,
            page: page
        });
    },
    switchForumPrompt: function (forum_id) {
        Layout.allianceForum.getWnd().ajaxRequestGet('alliance_forum', 'gotopage', {
            'forum_id': forum_id
        }, function (response, data, status) {
            if (status == "success") {
                Layout.dialogWindow.open(data.html, "Go to page", 300, 140, function () {}, true);
            }
        });
    },
    addPollOption: function () {
        var ele = $('#forum_poll_options_list'),
            children = ele.children('span.grepo_input'),
            fields = children.length;
        if (fields < 12) {
            var new_option = children.first().clone();
            ele.append(new_option);
            new_option.find('input').val('');
            $('#forum_post_textarea').height(220 + 54 - ele.outerHeight(true));
        }
    },
    markForumAsRead: function (forum_id) {
        Layout.allianceForum.getWnd().clearMenu();
        Layout.allianceForum.getWnd().requestContentPost('alliance_forum', 'forum', {
            'action': 'forum_read',
            'forum_id': forum_id
        });
    },
    markAllForumsAsRead: function (forum_id) {
        Layout.allianceForum.getWnd().ajaxRequestPost('alliance_forum', 'forum', {
            'action': 'forum_read_all',
            'forum_id': forum_id
        }, function () {
            $('span.forum_menu').removeClass('forum_unread');
        });
    },
    initAdmin: function () {
        $('#forum_list, #forum_shared_list').each(function () {
            $(this).sortable({
                axis: 'y',
                items: 'li',
                placeholder: 'forum ui-sortable-placeholder-highlight',
                helper: 'clone',
                handle: 'div.handle',
                containment: 'parent',
                start: function (ev, ui) {
                    Forum.old_index = $.inArray(ui.item[0], ui.item.parent().children());
                },
                stop: function (ev, ui) {
                    var new_index = $.inArray(ui.item[0], ui.item.parent().children());
                    var data = {};
                    data.forum_id = ui.item.attr('id').replace(/\D+/g, '');
                    data.old_index = Forum.old_index;
                    data.new_index = new_index;
                    data.shared_forum = ui.item.attr('id').indexOf('shared_id') >= 0;
                    Forum.moveForum(data);
                }
            });
        });
    },
    moveForum: function (options) {
        if (options.old_index == options.new_index) {
            return;
        }
        Layout.allianceForum.getWnd().clearMenu();
        Layout.allianceForum.getWnd().requestContentPost('alliance_forum', 'forum', {
            'action': 'forum_move',
            'forum_id': options.forum_id,
            'old_index': (options.old_index + 1),
            'new_index': (options.new_index + 1),
            'shared_forum': options.shared_forum
        });
    },
    forumUpdate: function (forum_id) {
        delete Forum.tboxes;
        var forum_name = $('#forum_forum_name_' + forum_id).attr('value');
        var forum_content = $('#forum_forum_content_' + forum_id).attr('value');
        var forum_hidden = $('#forum_forum_hidden_' + forum_id).attr('checked');
        Layout.allianceForum.getWnd().clearMenu();
        Layout.allianceForum.getWnd().requestContentPost('alliance_forum', 'forum', {
            'action': 'forum_update',
            'forum_id': forum_id,
            'forum_name': forum_name,
            'forum_content': forum_content,
            'forum_hidden': forum_hidden
        });
    },
    forumDelete: function (forum_id, confirmed) {
        if (!confirmed) {
            Layout.showConfirmDialog($('#forum_forum_name_' + forum_id).attr('value'), 'Do you really want to delete this forum?', function () {
                Forum.forumDelete(forum_id, true);
            });
        } else {
            Layout.allianceForum.getWnd().clearMenu();
            Layout.allianceForum.getWnd().requestContentPost('alliance_forum', 'forum', {
                'action': 'forum_delete',
                'forum_id': forum_id,
                'confirm': confirmed
            });
        }
    },
    forumCreate: function (confirmed) {
        if (!confirmed) {
            jQuery.blocker({
                caching: false,
                html: $('#create_forum_dialog'),
                width: 330,
                height: 200,
                success: '#create_forum_confirm',
                onSuccess: function () {
                    Forum.forumCreate(true);
                },
                cancel: '#create_forum_cancel'
            });
        } else {
            var forum_name = $('#forum_forum_name').attr('value');
            var forum_content = $('#forum_forum_content').attr('value');
            var forum_hidden = $('#forum_forum_hidden').attr('checked');
            Layout.allianceForum.getWnd().clearMenu();
            Layout.allianceForum.getWnd().requestContentPost('alliance_forum', 'forum', {
                'action': 'forum_create',
                'forum_name': forum_name,
                'forum_content': forum_content,
                'forum_hidden': forum_hidden
            });
        }
    },
    threadCreate: function () {
        var params = {};
        params.action = 'thread_create';
        params.forum_id = $('#forum_forum_id').attr('value');
        params.thread_name = $('#forum_thread_name').attr('value');
        params.thread_important = $('#forum_thread_important').attr('checked');
        params.post_text = $('#forum_post_textarea').attr('value');
        if ($('#forum_poll_question').length == 1) {
            params.poll_question = $('#forum_poll_question').attr('value');
            params.poll_show_result = $('#forum_poll_show_result').attr('checked');
            params.poll_option = [];
            $('#forum_poll_options_list input').each(function (i, elem) {
                params.poll_option[i] = $(elem).val();
            });
        }
        Layout.allianceForum.getWnd().sendMessage('go', params);
    },
    pollVote: function () {
        var thread_id = $('#forum_thread_id').attr('value');
        var poll_id = $('#forum_poll_id').attr('value');
        var poll_option_id = $('#forum_poll_wrapper input:radio:checked').attr('value');
        if (poll_option_id == undefined) {
            poll_option_id = 0;
        }
        Layout.allianceForum.getWnd().clearMenu();
        Layout.allianceForum.getWnd().requestContentPost('alliance_forum', 'forum', {
            'action': 'poll_vote',
            'thread_id': thread_id,
            'poll_id': poll_id,
            'poll_option_id': poll_option_id
        });
    },
    scrollPostsDown: function () {
        var postlist = $("#postlist"),
            scrollHeight = document.getElementById("postlist").scrollHeight,
            innerHeight = postlist.innerHeight();
        if (scrollHeight > innerHeight) {
            postlist.scrollTop(scrollHeight);
        }
    },
    postReply: function (thread_id, page) {
        var params = {};
        params.action = 'post_reply';
        params.thread_id = thread_id;
        params.page = page;
        Layout.allianceForum.getWnd().sendMessage('go', params, this.scrollPostsDown);
    },
    postSave: function () {
        var thread_id = $('#forum_thread_id').attr('value');
        var page = $('#forum_page').attr('value');
        var post_id = $('#forum_post_id').length == 1 ? $('#forum_post_id').attr('value') : 0;
        var post_text = $('#forum_post_textarea').attr('value');
        Layout.allianceForum.getWnd().clearMenu();
        Layout.allianceForum.getWnd().requestContentPost('alliance_forum', 'forum', {
            'action': 'post_save',
            'thread_id': thread_id,
            'page': page,
            'post_id': post_id,
            'post_text': post_text
        }, this.scrollPostsDown);
    },
    postQuote: function (thread_id, page, post_id) {
        var params = {};
        params.action = 'post_quote';
        params.thread_id = thread_id;
        params.post_id = post_id;
        params.page = page;
        Layout.allianceForum.getWnd().sendMessage('go', params);
    },
    postEdit: function (thread_id, page, post_id) {
        var params = {};
        params.action = 'post_edit';
        params.thread_id = thread_id;
        params.post_id = post_id;
        params.page = page;
        Layout.allianceForum.getWnd().sendMessage('go', params);
    },
    search: function (keywords, forum_id, page) {
        var params = {};
        params.action = 'search';
        params.keywords = encodeURIComponent(keywords);
        params.forum_id = forum_id;
        params.page = page || 1;
        Layout.allianceForum.getWnd().sendMessage('go', params);
    },
    initAutocomplete: function () {
        $('#share_forum_with_alliance #shared_with_name').oldautocomplete('/autocomplete', {
            'extraParams': {
                'what': 'game_alliance'
            },
            'minChars': 3,
            'autoFill': true
        });
    },
    togglePoll: function (options) {
        options = options || {};
        var poll = $('#forum_poll_wrapper');
        var list = $('#postlist');
        var reply = $('#post_save_wrapper');
        var footer = $('#forum div.game_list_footer');
        var bottom = footer.outerHeight(true) + reply.outerHeight(true);
        var top = parseInt(list.css('top'), 10);
        var visible = poll.is(':visible');
        var qr = (options.action === 'quote' || options.action === 'reply');
        var ruleset = {};

        function show() {
            poll.show();
            $('#poll_toggle span').toggleClass('collapsed', false).toggleClass('expanded', true);
        }

        function hide() {
            poll.hide();
            $('#poll_toggle span').toggleClass('collapsed', true).toggleClass('expanded', false);
        }
        if (options.show === false || visible) {
            hide();
        } else if (options.show || !visible) {
            show();
        }
    },
    setData: function (forum_datas) {
        this.forum_datas = forum_datas;
    },
    displayData: function () {
        $.each(this.forum_datas, function (forum_id, data) {
            var content = '';
            var jQElem = $('span.forum_id_' + forum_id);
            if (jQElem.length > 0 && (data.forum_content.length > 0 || data.alliances.length > 0)) {
                if (data.forum_content.length > 0) {
                    content = $('<div/>').text(data.forum_content).html() + '<br />';
                }
                if (data.alliances.length > 0) {
                    content += '<b>' + 'Shared with' + '</b>';
                    $.each(data.alliances, function (idx, name) {
                        content += '<br />' + name;
                    });
                }
                jQElem.mousePopup(new MousePopup(content));
            }
        });
    },
    toggleFunctionBar: function (action) {
        var top = $('#thread_functions');
        var list = $('#threadlist');
        var theight = $('#thread_functions_toggle').outerHeight(true) + $('#forumtitle').outerHeight(true);
        var height_top = 102;
        if (top.is(':hidden')) {
            top.show();
            top.animate({
                height: height_top.toString(10) + "px"
            }, 'normal');
            $('div.forum_toggle span').toggleClass('collapsed', false).toggleClass('expanded', true);
        } else {
            top.animate({
                height: "0px"
            }, 'normal', function () {
                top.hide();
            });
            $('div.forum_toggle span').toggleClass('collapsed', true).toggleClass('expanded', false);
        }
    },
    initForumPopups: function () {
        $('#admin_functions a.openclose_button.openclose_toggle').mousePopup(new MousePopup('Open/close marked threads'));
        $('#admin_functions a.cancel').mousePopup(new MousePopup('Delete marked threads'));
        $('#admin_functions #move a.button').mousePopup(new MousePopup('Move marked threads to the selected forum '));
        $('#threadlist a.forum_lastpost').mousePopup(new MousePopup('Go to last post'));
        $('#save_thread_title').mousePopup(new MousePopup('Save changes'));
        $('#forum_thread_name_span_text_admin').mousePopup(new MousePopup('Change title'));
        $('a.openclose_button.closed').mousePopup(new MousePopup('Open thread'));
        $('a.openclose_button.open').mousePopup(new MousePopup('Close thread'));
        $('#forum_buttons a.cancel').mousePopup(new MousePopup('Delete thread'));
        $('#forum_list a.cancel').mousePopup(new MousePopup('Delete forum'));
        $('#forumlist a.confirm').mousePopup(new MousePopup('Save changes'));
        $('li.shared_forum a.confirm').mousePopup(new MousePopup('Unlock forum'));
        $('div.text_box').mousePopup(new MousePopup('Rename'));
        $('#forumlist span.hidden_icon').mousePopup(new MousePopup('Hidden forum'));
        $('#forumlist span.share_icon').mousePopup(new MousePopup('Edit entries'));
        Forum.initAutocomplete();
    },
    initialize: function () {
        if (!this.flag_separate_forum_tab) {
            $('a.separate_forum_tab_link').show();
        }
        this.initForumPopups();
    }
};

var GameData = {
    units: [],
    powers: [],
    map_size: 0,
    add: function (data) {
        jQuery.extend(this, data);
    }
}

GPWindowMgr = function () {
    var wthat = this;
    var maxConcurrentOpen = 1;
    var TypeIDMAX = 0;
    var TypeInfo = [];

    function addWndType(name, taskbar_selector, wndhandler, _MaxConcurrentOpen, importantwindow) {
        wthat['TYPE_' + name] = TypeIDMAX;
        _MaxConcurrentOpen = _MaxConcurrentOpen || 0;
        importantwindow = importantwindow || false;
        TypeInfo[TypeIDMAX] = {
            handler: wndhandler,
            refs: [],
            tselector: (_MaxConcurrentOpen === 1 ? '#' : '.') + taskbar_selector,
            maxconcurrent: _MaxConcurrentOpen,
            isImportant: importantwindow
        };
        TypeIDMAX++;
    }
    addWndType('CUSTOM', null, WndHandlerDefault);
    addWndType('TOWNINDEX', 'city_index', WndHandlerTownIndex, 1, false);
    addWndType('FARM_TOWN', 'TODO', WndHandlerFarmTown);
    addWndType('WONDERS', 'TODO', WndHandlerWonders);
    addWndType('TOWN', 'taskbar_town_info', WndHandlerTown);
    addWndType('ISLAND', 'taskbar_island_info', WndHandlerIsland);
    addWndType('ALLIANCE', 'link_alliance', WndHandlerAlliance, 1);
    addWndType('ALLIANCE_FORUM', 'link_alliance_forum', WndHandlerAllianceForum, 1);
    addWndType('ALLIANCE_PROFILE', 'TODO', WndHandlerAllianceProfile, 1);
    addWndType('QUEST', 'TODO', WndHandlerQuest, 1, true);
    addWndType('BUILDING', 'taskbar_building', WndHandlerBuilding, 1);
    addWndType('MARKET', 'TODO', WndHandlerMarket, 1);
    addWndType('PHOENICIANSALESMAN', 'TODO', WndHandlerPhoenicianSalesman, 1);
    addWndType('RANKING', 'link_ranking', WndHandlerRanking, 1);
    addWndType('TOWN_OVERVIEWS', 'city_overview', WndHandlerTownOverviews, 1);
    addWndType('ATTACK_PLANER', 'link_planer', WndHandlerAttackPlaner, 1);
    addWndType('MESSAGE', 'link_messages', WndHandlerMessage, 1);
    addWndType('REPORT', 'link_report', WndHandlerReport, 1);
    addWndType('DIALOG', 'TODO', WndHandlerDialog, 0, true);
    addWndType('CONFIRM_DIALOG', 'TODO', WndHandlerConfirmDialog, 0, true);
    addWndType('UNINHABITED_PLACE', 'TODO', WndHandlerUninhabited, 1);
    addWndType('MEMO', 'memo_icon', WndHandlerMemo, 1);
    addWndType('PREMIUM', 'premium_link', WndHandlerPremium, 1);
    addWndType('PLAYER_PROFILE_EDIT', 'TODO', WndHandlerPlayerProfileEdit, 1);
    addWndType('PLAYER_PROFILE', 'b_profile', WndHandlerPlayerProfile, 1);
    addWndType('PLAYER_SETTINGS', 'b_settings', WndHandlerPlayerSettings, 1);
    addWndType('PUBLISH_REPORT', 'TODO', WndHandlerPublishReport, 1);
    addWndType('COLOR_TABLE', 'TODO', WndHandlerColorTable, 1);
    addWndType('CONQUEST', 'TODO', WndHandlerConquest, 1, true);
    addWndType('ATK_COMMAND', 'TODO', WndHandlerAtkCommand);
    addWndType('CONQUEROR', 'TODO', WndHandlerConqueror, 1);
    addWndType('CHAT', 'link_chat', WndHandlerChat, 1);
    addWndType('DIRECTION_XSELLING', 'TODO', WndHandlerDirectionXSelling, 1);
    addWndType('FARM_TOWN_OVERVIEWS', 'TODO', WndHandlerFarmTownOverviews, 1);
    addWndType('ACK_CHOOSE_TOWN', 'TODO', WndHandlerChooseDirection, 1);
    addWndType('SHOW_ON_LOGIN', 'taskbar_daily_reward', WndHandlerShowOnLogin, 1, true);
    addWndType('INVITE_FRIENDS', 'TODO', WndHandlerInviteFriends, 1);
    addWndType('PRESENTS', 'TODO', WndHandlerPresents, 1, false);
    var wndref = [],
        wndStack = [],
        window_rootid = 1000,
        window_nextid = window_rootid;
    this.setMaxConcurrent = function (limit) {
        maxConcurrentOpen = limit;
    };
    this.Create = function (type, title, UIopts) {
        var handler, i;
        if (maxConcurrentOpen != 0 && !TypeInfo[type].isImportant && wndStack.length >= maxConcurrentOpen) {
            i = wndStack.length;
            while (i--) {
                if (!TypeInfo[wndStack[i].type].isImportant) {
                    wndStack[i].close();
                }
            }
        }
        if ((TypeInfo[type].maxconcurrent > 0) && (TypeInfo[type].refs.length >= TypeInfo[type].maxconcurrent)) {
            i = TypeInfo[type].refs.length;
            while (i--) {
                TypeInfo[type].refs[i].toTop();
            }
            return null;
        }
        var wnd = new GPWindow(window_nextid++, this);
        handler = new TypeInfo[type].handler(wnd);
        wnd.setHandler(handler);
        var args = [];
        for (i = 1; i < arguments.length; i++) {
            args[i - 1] = arguments[i];
        }
        if (!wnd.createWindow.apply(wnd, args)) {
            wnd = null;
            handler = null;
            return null;
        }
        wndref[wnd.getID() - window_rootid] = wnd;
        TypeInfo[type].refs.push(wnd);
        wnd.typeinforefid = TypeInfo[type].refs.length - 1;
        wnd.type = type;
        wndStack.push(wnd);
        if (wndStack.length > 1) {
            if (Layout instanceof GPLayout) {
                $('#gpwindow_area_left').show();
            } else {
                $('#close_all_windows').parent().stop().fadeIn();
            }
        }
        return wnd;
    };
    this.Unregister = function (wnd) {
        TypeInfo[wnd.type].refs.remove(wnd.typeinforefid);
        wndref[wnd.getID() - window_rootid] = undefined;
        var i = wndStack.length;
        while (i--) {
            if (wndStack[i] == wnd) {
                wndStack.remove(i);
                break;
            }
        }
        if (Layout instanceof GPLayout) {
            if (wndStack.length < 2 && $('#gpwindow_area').children().length < 2) {
                $('#gpwindow_area_left').hide();
            }
        } else {
            $('#close_all_windows').parent().stop().fadeOut();
        }
    };
    this.getTypeInfo = function (id) {
        return TypeInfo[id];
    };
    this.numOpen = function () {
        return wndStack.length;
    };
    this.closeAll = function () {
        var wndstack = [],
            t = TypeInfo.length,
            w, i;
        while (t--) {
            w = TypeInfo[t].refs.length;
            while (w--) {
                wndstack.push(TypeInfo[t].refs[w]);
            }
        }
        i = wndstack.length;
        while (i--) {
            wndstack[i].close();
            wndstack[i] = undefined;
        }
    };
    this.GetByID = function (id) {
        var ref = wndref[id - window_rootid];
        if (!ref) {
            return null;
        }
        return ref;
    };
    this.is_open = function (type) {
        return this.getOpen(type).length;
    };
    this.getAllOpen = function () {
        return wndStack;
    };
    this.getOpen = function (type) {
        return TypeInfo[type].refs;
    };
    this.getOpenFirst = function (type) {
        if (TypeInfo[type].refs.length > 0) {
            return TypeInfo[type].refs[0];
        }
        return null;
    };
    return this;
}.call({});
GPWindowMgr.extendLayoutWithShortLinks = function (context) {
    function GenericWindowThing(type) {
        this.open = function () {
            var w = GPWindowMgr.getOpenFirst(type),
                args = Array.prototype.slice.call(arguments);
            if (!w) {
                args[0] = type;
                GPWindowMgr.Create.apply(GPWindowMgr, args);
            } else {
                w.sendMessage('openIndex');
                w.toTop();
            }
        };
        this.close = function (invokedbywindow) {
            var w = GPWindowMgr.getOpenFirst(type);
            if (!w) {
                return;
            }
            if (!invokedbywindow) {
                w.close();
            }
        };
        this.getWnd = function () {
            return GPWindowMgr.getOpenFirst(type);
        };
    }
    context.townIndex = function () {
        var tut_senate_arrow = null;
        this.open = function (town_id, town_name) {
            var w = GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_TOWNINDEX);
            if (!w) {
                GPWindowMgr.Create(GPWindowMgr.TYPE_TOWNINDEX, 'City view' + ' - ' + town_name, {}, town_id, town_name);
            } else {
                w.sendMessage('setTown', town_id, town_name);
                w.toTop();
            }
        };
        this.close = function (invokedbywindow) {
            var w = null;
            w = GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_TOWNINDEX);
            if (!w) {
                return;
            }
            if (!invokedbywindow) {
                w.close();
            }
        };
        this.getWnd = function () {
            return GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_TOWNINDEX);
        };
        this.showTutorialArrowOnSenate = function () {
            var w = GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_TOWNINDEX);
            if (w) {
                tut_senate_arrow = Tutorial.makeArrow('#index_main', 's', {
                    x: -15,
                    y: 5
                });
                tut_senate_arrow.bounce();
            }
        };
        this.stopTutorialArrowOnSenate = function () {
            if (tut_senate_arrow != null) {
                tut_senate_arrow.remove();
                tut_senate_arrow = null;
            }
        };
        return this;
    }.call({});
    context.buildingWindow = {
        open: function (building, isBuilding, action, params) {
            var w = this.getWnd(),
                type, title, handler, options = {};
            action = action || context.buildingWindow.getDefaultAction(building);
            if (building === 'place' && action === 'simulator') {
                options.units = params;
            } else {
                options = params;
            }
            if (w) {
                handler = w.getHandler();
                if (building === 'market' && handler instanceof WndHandlerBuilding) {
                    w.setHandler(new WndHandlerMarket(w));
                } else if (building !== 'market' && handler instanceof WndHandlerMarket) {
                    w.setHandler(new WndHandlerBuilding(w));
                }
                w.clearMenuNow();
                w.requestContentGet(GameData.buildings[building].controller, action, options, function () {
                    handler.setToTop();
                });
            } else {
                title = 'Building view' + ' - ' + GameData.buildings[building].name + ' (' + Game.townName + ')';
                type = (building === 'market' ? GPWindowMgr.TYPE_MARKET : GPWindowMgr.TYPE_BUILDING);
                GPWindowMgr.Create(type, title, {}, building, action, options);
            }
        },
        refresh: function () {
            var w = this.getWnd(),
                handler;
            w.getHandler().refresh();
        },
        getDefaultAction: function (building) {
            return building == "place" ? 'culture' : "index";
        },
        getWnd: function () {
            return GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_BUILDING) || GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_MARKET);
        }
    };
    context.conquestWindow = function () {};
    context.conquestWindow.prototype = new GenericWindowThing(GPWindowMgr.TYPE_CONQUEST);
    context.conquestWindow.prototype.constructor = GenericWindowThing;
    context.conquestWindow = new context.conquestWindow();
    context.allianceForum = function () {};
    context.allianceForum.prototype = new GenericWindowThing(GPWindowMgr.TYPE_ALLIANCE_FORUM);
    context.allianceForum.prototype.constructor = GenericWindowThing;
    context.allianceForum = new context.allianceForum();
    context.newMessage = function () {
        this.open = function (recipients) {
            var w = GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_MESSAGE);
            if (!recipients) {
                HumanMessage.error('The recipient list is empty.');
                return;
            }
            if (!w) {
                GPWindowMgr.Create(GPWindowMgr.TYPE_MESSAGE, 'Messages', {}, 'new', recipients);
            } else {
                w.toTop();
                w.sendMessage('messageNew', recipients);
            }
        };
    };
    context.newMessage.prototype = new GenericWindowThing(GPWindowMgr.TYPE_MESSAGE);
    context.newMessage.prototype.constructor = GenericWindowThing;
    context.newMessage = new context.newMessage();
    context.allianceProfile = function () {
        this.open = function (alliance_name, alliance_id) {
            var w = GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_ALLIANCE_PROFILE);
            if (!w) {
                context.wnd.Create(context.wnd.TYPE_ALLIANCE_PROFILE, alliance_name, {
                    alliance_id: alliance_id
                });
            } else {
                w.toTop();
                w.requestContentGet('alliance', 'profile', {
                    alliance_id: alliance_id
                });
                w.setTitle(alliance_name);
            }
        };
    };
    context.allianceProfile.prototype = new GenericWindowThing(GPWindowMgr.TYPE_ALLIANCE_PROFILE);
    context.allianceProfile.prototype.constructor = GenericWindowThing;
    context.allianceProfile = new context.allianceProfile();
    context.playerProfile = function () {
        this.open = function (player_name, player_id) {
            var w = GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_PLAYER_PROFILE);
            if (!w) {
                context.wnd.Create(context.wnd.TYPE_PLAYER_PROFILE, player_name, {
                    player_id: player_id
                });
            } else {
                w.toTop();
                w.requestContentGet('player', 'get_profile_html', {
                    player_id: player_id
                });
                w.setTitle(player_name);
            }
        };
    };
    context.playerProfile.prototype = new GenericWindowThing(GPWindowMgr.TYPE_PLAYER_PROFILE);
    context.playerProfile.prototype.constructor = GenericWindowThing;
    context.playerProfile = new context.playerProfile();
    context.phoenicianSalesman = function () {};
    context.phoenicianSalesman.prototype = new GenericWindowThing(GPWindowMgr.TYPE_PHOENICIANSALESMAN);
    context.phoenicianSalesman.prototype.constructor = GenericWindowThing;
    context.phoenicianSalesman = new context.phoenicianSalesman();
    context.dialogWindow = {
        open: function (content, title, width, height, onClose, minimizable) {
            var w = GPWindowMgr.Create(GPWindowMgr.TYPE_DIALOG, 'Information', {
                onClose: onClose,
                minimizable: minimizable
            });
            w.setTitle(title);
            width = width || 400;
            height = height || 250;
            w.setSize(width, height);
            w.setContent(content);
            return w;
        },
        close: function (wnd_id) {
            var w = GPWindowMgr.GetByID(wnd_id);
            if (!w) {
                return;
            }
            if (w.type === GPWindowMgr.TYPE_DIALOG) {
                w.close();
            }
        }
    };
    context.publishReportWindow = {
        open: function (content, report_id) {
            var w = GPWindowMgr.Create(GPWindowMgr.TYPE_PUBLISH_REPORT, 'Publish report', {});
            w.setSize(500, 350);
            w.setContent(content);
        },
        close: function (wnd_id) {
            var w = GPWindowMgr.GetByID(wnd_id);
            if (!w) {
                return;
            }
            if (w.type === GPWindowMgr.TYPE_PUBLISH_REPORT) {
                w.close();
            }
        }
    };
};

function GPWindow(id, wndmgr) {
    var wndid = id,
        wndname = 'gpwnd_' + id,
        rootnode, ajaxloader, loader, jqrootnode, jqcontent, wndhandler = null,
        that = this,
        frame, options, minElm = {},
        menuScroll = false,
        clear_menu_flag = false,
        minimized = false,
        cachedArguments, menuData;
    var header;
    this.setHandler = function (Handler) {
        wndhandler = Handler;
    };
    this.getHandler = function () {
        return wndhandler;
    };
    this.createWindow = function (title, UIopts) {
        options = UIopts || (UIopts = {});
        rootnode = document.createElement('div');
        rootnode.id = wndname;
        rootnode.className = 'gpwindow_content';
        frame = document.createElement('div');
        frame.className = 'gpwindow_frame';
        var left = document.createElement('div'),
            right = document.createElement('div'),
            bottomCenter = document.createElement('div'),
            bottomLeft = document.createElement('div'),
            bottomRight = document.createElement('div'),
            topCenter = document.createElement('div'),
            topLeft = document.createElement('div'),
            topRight = document.createElement('div'),
            minimizeElem, help;
        left.className = 'gpwindow_left';
        right.className = 'gpwindow_right';
        bottomLeft.className = 'gpwindow_left corner';
        bottomRight.className = 'gpwindow_right corner';
        bottomCenter.className = 'gpwindow_bottom';
        topLeft.className = 'gpwindow_left corner';
        topRight.className = 'gpwindow_right corner';
        topCenter.className = 'gpwindow_top';
        bottomCenter.appendChild(bottomLeft);
        bottomCenter.appendChild(bottomRight);
        topCenter.appendChild(topLeft);
        topCenter.appendChild(topRight);
        frame.appendChild(left);
        frame.appendChild(right);
        frame.appendChild(bottomCenter);
        frame.appendChild(topCenter);
        frame.appendChild(rootnode);
        ajaxloader = document.createElement('img');
        ajaxloader.id = 'window_ajax_loader';
        ajaxloader.src = 'http://cdn.grepolis.com/images/game/ajax-loader.gif';
        ajaxloader.alt = '';
        ajaxloader.style.visibility = 'hidden';
        rootnode.appendChild(ajaxloader);
        jqrootnode = $(frame);
        jqcontent = $(rootnode);
        jqcontent.wnd = this;
        loader = new gpAjax(this, false);
        if (wndhandler) {
            if (!wndhandler.onInit.apply(wndhandler, arguments)) {
                loader = null;
                return false;
            }
            var defaults = wndhandler.getDefaultWindowOptions();
            $.extend(defaults, UIopts);
            UIopts = options = defaults;
            ajaxloader.style.top = ((defaults.height >> 1) - 58) + 'px';
            ajaxloader.style.left = ((defaults.width >> 1) - 29) + 'px';
            ajaxloader.style.position = 'relative';
        }
        if (title && title.length > 0) {
            UIopts.title = title;
        }
        if (UIopts.fullwindow) {
            rootnode.className += ' fullwindow';
        }
        if (UIopts.menuScroll) {
            menuScroll = UIopts.menuScroll;
        }
        jqrootnode.dialog(UIopts);
        if (UIopts.minimizable) {
            minimizeElem = $('<a>').attr({
                'href': '#',
                'class': 'ui-dialog-titlebar-minimize ui-corner-all'
            }).click(function () {
                that.minimize.apply(that, arguments);
            });
            jqrootnode.prev().append(minimizeElem);
        }
        if (UIopts.help) {
            help = $('<a>').attr({
                'href': '#',
                'class': 'ui-dialog-titlebar-help ui-corner-all'
            }).click(this.showHelp);
            jqrootnode.prev().append(help);
        }
        if (UIopts.yOverflowHidden) {
            jqcontent.css({
                'overflow-y': 'hidden'
            });
        }
        jqrootnode.bind('dialogbeforeclose', function () {
            return wndhandler.onClose();
        });
        jqrootnode.bind('dialogclose', function (event, ui) {
            wndmgr.Unregister(jqcontent.wnd);
            jqrootnode.dialog('destroy');
            if (header) {
                header.find('div.menu_wrapper').find('a').unbind();
            }
            if (jqrootnode.parent()) {
                jqrootnode.remove();
            }
            loader = null;
            wndhandler = null;
        });
        jqrootnode.bind('dialogfocus', function (event, ui) {
            wndhandler.onFocus();
        });
        var min_top = 92;
        if (jqrootnode.parent().position().top < min_top && !UIopts.overrideMinTop) {
            jqrootnode.parent().css('top', min_top + 'px');
        }
        return true;
    };
    this.close = function () {
        that.maximize();
        jqcontent.empty();
        jqrootnode.dialog('close');
        if (Layout.Countdown) {
            window.setTimeout(Layout.Countdown.autoClean, 150);
        }
    };
    this.showHelp = function () {
        return wndhandler.showHelp.apply(wndhandler, arguments);
    };
    this.destroy = function () {
        jqrootnode.dialog('destroy');
    };
    this.setPosition = function (pos) {
        jqrootnode.dialog('option', 'position', pos);
    };
    this.setSize = function (width, height) {
        jqrootnode.dialog('option', 'width', width);
        jqrootnode.dialog('option', 'height', height);
    };
    this.autoResize = function (data) {
        var tmp, elem, arr = [],
            newheight, diff;
        tmp = data.split(/<script.*?>[\s\S]*?<\/.*?script>/gi).join('');
        elem = jqcontent.clone().html(tmp).appendTo('body').css({
            'width': jqcontent.width(),
            'zIndex': 0,
            'left': -5000,
            'position': 'relative'
        });
        elem.children().each(function () {
            var child = $(this);
            arr.push(child.position().top + child.outerHeight(true));
        });
        newheight = arr.max(arr) + jqrootnode.children('div.gpwindow_top').height() + jqrootnode.children('div.gpwindow_bottom').height();
        this.setHeight(Math.max(newheight, options.minHeight || 0));
        if ($.browser.msie && +$.browser.version === 7) {
            diff = Math.abs(newheight - jqrootnode.height());
            jqrootnode.css('height', newheight + diff);
        }
        elem.remove();
        elem = undefined;
        return data;
    };
    this.setHeight = function (height) {
        jqrootnode.dialog('option', 'height', height);
    };
    this.setWidth = function (width) {
        jqrootnode.dialog('option', 'width', width);
    };
    this.getHeight = function () {
        return jqrootnode.dialog('option', 'height');
    };
    this.getWidth = function () {
        return jqrootnode.dialog('option', 'width');
    };
    this.setMinSize = function (minWidth, minHeight) {
        jqrootnode.dialog('option', 'minWidth', minWidth);
        jqrootnode.dialog('option', 'minHeight', minHeight);
    };
    this.setMinHeight = function (minHeight) {
        jqrootnode.dialog('option', 'minHeight', minHeight);
    };
    this.setMinWidth = function (minWidth) {
        jqrootnode.dialog('option', 'minWidth', minWidth);
    };
    this.getMinHeight = function () {
        return jqrootnode.dialog('option', 'minHeight');
    };
    this.getMinWidth = function () {
        return jqrootnode.dialog('option', 'minWidth');
    };
    this.setMaxSize = function (maxWidth, maxHeight) {
        jqrootnode.dialog('option', 'maxWidth', maxWidth);
        jqrootnode.dialog('option', 'maxHeight', maxHeight);
    };
    this.setMaxHeight = function (maxHeight) {
        jqrootnode.dialog('option', 'maxHeight', maxHeight);
    };
    this.setMaxWidth = function (maxWidth) {
        jqrootnode.dialog('option', 'maxWidth', maxWidth);
    };
    this.getMaxHeight = function () {
        return jqrootnode.dialog('option', 'maxHeight');
    };
    this.getMaxWidth = function () {
        return jqrootnode.dialog('option', 'maxWidth');
    };
    this.allowResize = function (allowed) {
        jqrootnode.dialog('option', 'resizable', allowed);
    };
    this.getZIndex = function () {
        return jqrootnode.dialog('option', 'zIndex');
    };
    this.setZIndex = function (z) {
        return jqrootnode.dialog('option', 'zIndex', z);
    };
    this.toTop = function () {
        that.maximize.apply(that, arguments);
        jqrootnode.dialog('moveToTop');
    };
    this.getPosition = function () {
        var rect = {
            position: jqrootnode.dialog('option', 'position'),
            width: jqrootnode.dialog('option', 'width'),
            height: jqrootnode.dialog('option', 'height')
        };
        return rect;
    };
    this.setDialogClass = function (dialogClass) {
        jqrootnode.dialog('option', 'dialogClass', dialogClass);
    };
    this.setTitle = function (title) {
        options.title = title;
        jqrootnode.dialog('option', 'title', title);
    };
    this.setContent = function (data) {
        if (options.autoresize) {
            data = this.autoResize(wndhandler.onSetContent(data));
        } else {
            data = wndhandler.onSetContent(data);
        }
        jqcontent.html(data);
    };
    this.setContent2 = function (data) {
        jqcontent.html(data);
    };
    this.appendContent = function (data) {
        jqcontent.append(data);
    };
    this.initMenu = function (data, controller, action) {
        var args = arguments;
        menuData = data || menuData;
        var menu = $.parseJSON(menuData);
        header = jqrootnode.dialog('widget').find('div.ui-widget-header');
        var title_width = header.find('span.ui-dialog-title').outerWidth(true),
            list = document.createElement('ul'),
            html = document.createElement('div'),
            link, li, wrapper, elements = [],
            naturalWidth = [],
            width_factor = 1.25;

        function arrangeScroll() {
            var button_sum, titlebar, tab_width;
            prev = $('<a href="#" class="prev"></a>');
            next = $('<a href="#" class="next"></a>');
            max_scroll = menu_width - total_width;
            step = 100;
            lf = document.createElement('div');
            rf = document.createElement('div');
            lf.className = 'fade_left';
            rf.className = 'fade_right';
            html.className = html.className += ' menu_wrapper_scroll';
            html.appendChild(lf);
            html.appendChild(rf);
            button_sum = 28 + 21 + 21;
            if (title_width + button_sum > header.width() || !menu_width) {
                titlebar = header.find('span.ui-dialog-title');
                title_width = titlebar.outerWidth(true);
                tab_width = total_width * width_factor + button_sum;
                if (title_width - tab_width < 0) {
                    titlebar.css('width', header.width() / 2).attr('title', titlebar.text());
                } else {
                    titlebar.css('width', title_width - tab_width + 'px').attr('title', titlebar.text());
                }
                return that.initMenu.apply(that, args);
            }
            header.append(next).append(prev);
            var magicnumber = 1;
            list.style.width = total_width + magicnumber + 'px';
            list.style.right = max_scroll - step + 'px';
            prev.click(function () {
                var right = parseInt(list.style.right, 10);
                if (right >= max_scroll - step) {
                    list.style.right = right - step + 'px';
                }
                return false;
            });
            next.click(function () {
                var right = parseInt(list.style.right, 10);
                if (right <= step) {
                    list.style.right = right + step + 'px';
                }
                return false;
            });
        }

        function arrangeShort() {
            j = elements.length;
            while (j--) {
                var k = naturalWidth.length;
                new_w = naturalWidth[j][1] ? Math.floor(max_width) : naturalWidth[j][0];
                count = naturalWidth.length;
                while (k--) {
                    if (naturalWidth[k][1] == false && k != j) {
                        incr = ((max_width - foo[k]) / count);
                        foo[k] -= incr;
                        if (new_w + incr > naturalWidth[j][0]) {
                            count--;
                            naturalWidth[j][1] = false;
                        } else {
                            new_w += incr;
                        }
                    }
                }
                if (new_w < (naturalWidth[j][0])) {
                    dots = document.createElement('span');
                    dots.className = 'submenu_dots';
                    dots.innerHTML = '\u2026';
                    text = elements[j].childNodes[0].childNodes[0].childNodes[0].childNodes[0];
                    text.appendChild(dots);
                    text.style.width = new_w - 24 + 'px';
                    text.style.marginRight = '16px';
                }
            }
        }
        wrapper = header.find('div.menu_wrapper');
        if (clear_menu_flag && wrapper.length) {
            clear_menu_flag = false;
            header.find('div.menu_wrapper, a.prev, a.next').remove();
            wrapper = [];
        } else if (wrapper.length) {
            return false;
        }
        html.className = 'menu_wrapper' + (options.minimizable ? ' minimize' : '') + (options.help ? ' help' : '');
        html.style.left = title_width + 'px';
        list.className = 'menu_inner';
        list.style.width = '5000px';
        var i, title, clickHandler = function fooooooo() {
                var i = this.id,
                    onclick = menu[i].onclick,
                    action, controller, obj;
                if (menu[i].id) {
                    if (onclick) {
                        onclick = typeof onclick == Object ? onclick : onclick.replace(/"/g, '').split(/,|\(|\)/g);
                        wndhandler.wnd[onclick.shift()].apply(this, onclick);
                    }
                } else {
                    action = menu[i].action;
                    controller = menu[i].controller;
                    obj = menu[i].type ? {
                        'type': menu[i].type
                    } : menu[i].obj;
                    wndhandler.wnd.requestContentGet(controller, action, obj);
                }
                var tmp, mapFunction = function (i) {
                        tmp = i.firstChild;
                        i.removeChild(tmp);
                        tmp.className = tmp.className.replace(' active', '');
                        i.appendChild(tmp);
                    };
                elements.map(mapFunction);
                this.className += ' active';
            };
        for (i in menu) {
            title = menu[i].name.replace(/&nbsp;/, ' ').strip();
            link = document.createElement('a');
            link.innerHTML = '<span class="left"><span class="right"><span class="middle" title="' + title + '">' + menu[i].name + '</span></span></span>';
            link.className = 'submenu_link' + (menu[i].className ? ' ' + menu[i].className : '');
            link.href = '#';
            link.id = i;
            if (i == controller + '-' + action) {
                link.className += ' active';
            }
            $(link).click(clickHandler);
            li = document.createElement('li');
            li.appendChild(link);
            list.insertBefore(li, list.firstChild);
            elements.push(li);
        }
        html.appendChild(list);
        header.append(html);
        var menu_width = $(html).width(),
            max_width = Math.ceil(menu_width / elements.length),
            total_width = 0,
            width, j = elements.length;
        while (j--) {
            width = $(elements[j]).outerWidth(true);
            total_width += (naturalWidth[j] = [$(elements[j]).outerWidth(true), width > max_width])[0];
        }
        var foo = naturalWidth.map(function (i) {
            return i[0];
        }, this),
            new_w, count, dots, text, incr, next, prev, max_scroll, rf, lf, step;
        if (menu_width && menu_width > total_width) {
            return;
        }
        header[0].removeChild(html);
        if (menu_width * width_factor > total_width && !menuScroll) {
            arrangeShort();
        } else {
            arrangeScroll();
        }
        header.append(html);
    };
    this.clearMenu = function () {
        clear_menu_flag = true;
    };
    this.clearMenuNow = function () {
        if (jqrootnode) {
            var header = jqrootnode.dialog('widget').find('div.ui-widget-header');
            var wrapper = header.find('div.menu_wrapper');
            if (wrapper.length) {
                header.find('div.menu_wrapper, a.prev, a.next').remove();
            }
        }
    };
    this.sendMessage = function (data, callback) {
        return wndhandler.onMessage.apply(wndhandler, arguments, callback);
    };
    this.call = function () {
        var args = Array.prototype.slice.call(arguments);
        return wndhandler[args.shift()].apply(wndhandler, (args));
    };
    this.ajaxRequest = function (controller, action, params, callback, method) {
        var obj;
        var callback_success = null;
        var callback_error = null;
        if (typeof callback == 'object') {
            callback_success = callback.success ? callback.success : null;
            callback_error = callback.error ? callback.error : null;
        } else {
            callback_success = callback;
        }
        if (!params) {
            params = {
                town_id: Game.townId
            };
        } else if (!params.town_id) {
            params.town_id = Game.townId;
        }
        params.nlreq_id = Game.notification_last_requested_id;
        obj = {
            success: function (_wnd, _data, _flag) {
                if (callback_success && hasValidTownToken(_data)) {
                    callback_success(_wnd, _data, _flag);
                }
            },
            error: function (_wnd, _data) {
                if (!hasValidTownToken(_data)) {
                    return;
                }
                if (callback_error) {
                    callback_error(_wnd, _data);
                }
                if (_data.error) {
                    wndhandler.onRcvDataError(_data.error);
                }
                if (_data.gpWindowclose === true) {
                    _wnd.close();
                }
            }
        };
        if (method == 'get') {
            loader.tmpGet(controller, action, params, true, obj);
        } else if (method == 'post') {
            loader.tmpPost(controller, action, params, true, obj);
        } else {
            if (method !== undefined) {
                debug('check & remove old 6th param (used for ajax-call-locking) of ajax call!!!');
            }
            loader.post(controller, action, params, true, obj);
        }
    };
    this.ajaxRequestGet = function (controller, action, params, callback) {
        this.ajaxRequest(controller, action, params, callback, 'get');
    };
    this.ajaxRequestPost = function (controller, action, params, callback) {
        this.ajaxRequest(controller, action, params, callback, 'post');
    };
    this.showAjaxLoader = function () {
        ajaxloader.style.visibility = 'visible';
    };
    this.hideAjaxLoader = function () {
        ajaxloader.style.visibility = 'hidden';
    };
    this.isMaintenanceBlocked = function () {
        return (typeof Layout !== 'undefined') ? Layout.isMaintenanceBlocked() : false;
    };
    this.reloadContent = function () {
        if (!cachedArguments) {
            throw 'No controller or action!';
        }
        this.requestContent.apply(this, cachedArguments);
    };
    this.requestContent = function (controller, action, params, callback_success, external_reload, method) {
        var obj;
        cachedArguments = arguments;
        if (!params) {
            params = {
                town_id: Game.townId
            };
        } else if (!params.town_id) {
            params.town_id = Game.townId;
        }
        params.nlreq_id = Game.notification_last_requested_id;
        if (!jqrootnode.find('div.grey_layer').length) {
            jqrootnode.append($('<div class="grey_layer"></div>'));
        }
        obj = {
            success: function (_wnd, _data, _flag) {
                if (!hasValidTownToken(_data)) {
                    return;
                }
                jqrootnode.find('div.grey_layer').remove();
                if (_data.tutorial_error != undefined) {
                    HumanMessage.error(_data.tutorial_error);
                    _wnd.close();
                } else {
                    if (_data.menu || external_reload) {
                        try {
                            that.clearMenuNow();
                            _wnd.initMenu.apply(_wnd, [_data.menu || menuData, controller, action]);
                        } catch (e) {}
                    }
                    wndhandler.onRcvData(_data, controller, action);
                }
                if (callback_success) {
                    callback_success(_wnd, _data, _flag);
                }
            },
            error: function (_wnd, _data) {
                if (!hasValidTownToken(_data)) {
                    return;
                }
                jqrootnode.find('div.grey_layer').remove();
                if (_data.error && wndhandler) {
                    wndhandler.onRcvDataError(_data.error);
                }
                if (_data && _data.gpWindowclose === true) {
                    _wnd.close();
                }
            }
        };
        if (method == 'get') {
            loader.tmpGet(controller, action, params, true, obj);
        } else if (method == 'post') {
            loader.tmpPost(controller, action, params, true, obj);
        } else {
            if (method !== undefined) {
                debug('check & remove old 6th param (used for ajax-call-locking) of ajax call!!!');
            }
            loader.post(controller, action, params, true, obj);
        }
    };
    this.requestContentPost = function (controller, action, params, callback_success, external_reload) {
        this.requestContent(controller, action, params, callback_success, external_reload, 'post');
    };
    this.requestContentGet = function (controller, action, params, callback_success, external_reload) {
        this.requestContent(controller, action, params, callback_success, external_reload, 'get');
    };
    var tutorial_arrow_at_closebutton = null;
    this.showTutorialArrowOnCloseButton = function () {
        tutorial_arrow_at_closebutton = Tutorial.makeArrow(jqrootnode.parent().find('.ui-dialog-titlebar-close.ui-corner-all'), 's', {
            x: 0,
            y: 2
        });
        tutorial_arrow_at_closebutton.bounce();
    };
    this.stopTutorialArrowOnCloseButton = function () {
        if (tutorial_arrow_at_closebutton != null) {
            tutorial_arrow_at_closebutton.remove();
            tutorial_arrow_at_closebutton = null;
        }
    };
    this.getID = function () {
        return wndid;
    };
    this.getName = function () {
        return wndname;
    };
    this.getTitle = function () {
        return options.title;
    };
    this.getElement = function () {
        return rootnode;
    };
    this.getJQElement = function () {
        return jqrootnode;
    };
    this.getJQCloseButton = function () {
        return jqrootnode.parent().find('.ui-dialog-titlebar-close.ui-corner-all');
    };
    this.isMinimized = function () {
        return minimized;
    };
    this.getMinimized = function () {
        return !minimized ? false : minElm.html;
    };
    this.minimizeWindow = function () {
        var html = minElm.html = $('<div id="' + jqcontent[0].id + '_min" class="gpwindow_minimize left"><div class="right"><div class="center"></div></div></div>'),
            close = minElm.close = $('<a>').attr({
                'href': '#',
                'class': 'ui-dialog-titlebar-close ui-corner-all'
            }).click(that.close),
            max = minElm.max = $('<a>').attr({
                'href': '#',
                'class': 'ui-dialog-titlebar-maximize ui-corner-all'
            }).click(that.maximizeWindow),
            title = minElm.title = $('<span>').attr({
                'href': '#',
                'class': 'ui-dialog-title'
            }).css({
                'cursor': 'pointer'
            }).html(options.title).click(that.maximizeWindow),
            area = minElm.area = $('#gpwindow_area').show();
        html.find('div.center').append(title).append(close).append(max);
        area.append(html).parent().parent().show();
        html.fadeIn();
        jqrootnode.parent().hide();
        minimized = true;
    };
    this.maximizeWindow = function () {
        jqrootnode.parent().show();
        if (!minElm.html) {
            return;
        }
        minElm.html.remove();
        minElm.close.unbind();
        minElm.max.unbind();
        minElm.title.unbind();
        if (minElm.area.children().length < 2 && wndmgr.numOpen() < 2) {
            minElm.area.parent().parent().hide();
        }
        minElm = {};
        that.toTop();
        minimized = false;
    };
}
GPWindow.prototype.minimize = function () {
    this.minimizeWindow();
};
GPWindow.prototype.maximize = function () {
    this.maximizeWindow();
};

function ImageCountdown(object, start_time, end_time, css_options, css_image_options, callback) {
    var that = this;
    var tick_interval = null;
    var img = null;
    var duration = (end_time - start_time) * 1000;
    var count_pictures = 64;

    function start() {
        var div = $('<div>').addClass('image_countdown').css(css_options);
        img = $('<img>').attr('src', 'http://cdn.grepolis.com/images/game/order/order_layer.png').css(css_image_options);
        object.after(div.append(img));
        if (!tick_interval) {
            tick_interval = window.setInterval(that.tick, 1E3);
        }
    }
    this.tick = function () {
        var now = Date.parse(new Date());
        var already = now - start_time * 1E3;
        var percent = (already * 100) / duration;
        var picture_number = Math.round(percent * count_pictures / 100);
        img.css('top', (picture_number * -parseInt(css_image_options.width)) + 'px');
        if (already >= duration) {
            picture_number = count_pictures;
            that.stop();
            if (callback !== undefined && typeof callback === 'function') {
                callback();
            }
        }
    };
    this.stop = function () {
        window.clearInterval(tick_interval);
        tick_interval = null;
        start_time = 0;
        end_time = 0;
        duration = 0;
    }
    start();
}

var NotificationType = {
    NOTASSIGNED: 'notassigned',
    SYSTEMMESSAGE: 'systemmessage',
    NEWMESSAGE: 'newmessage',
    AWMESSAGE: 'awmessage',
    NEWTEMPREPORT: 'newtempreport',
    NEWREPORT: 'newreport',
    NEWALLIANCEPOST: 'newalliancepost',
    NEWAWARD: 'newaward',
    RESOURCETRANSPORT: 'resourcetransport',
    QUEST: 'quest',
    INCOMING_ATTACK: 'incoming_attack',
    INCOMING_SUPPORT: 'incoming_support',
    PLANED_ATTACK: 'planed_attack',
    PHOENICIAN_SALESMAN_ARRIVED: 'phoenician_salesman_arrived',
    PHOENICIAN_SALESMAN_LEAVE: 'phoenician_salesman_leave',
    ALLIANCE_INVITATION: 'alliance_invitation',
    BUILDING_FINISHED: 'building_finished',
    ALL_BUILDING_FINISHED: 'all_building_finished',
    SHOW_CAPTCHA: 'show_captcha',
    SHOW_CURATOR_POPUP: 'curator_popup'
};
var NotificationStack = function notificationStack() {
        var layout_reserved_height = 135;
        var additional_reserved_height = 0;
        var unitbar_reserved_height = 0;
        var max_notifications = 5;
        var hidden = [];
        var spawned = [];
        var that = this;
        this.length = function length() {
            return hidden.length + spawned.length;
        };
        this.loop = function (callback) {
            var i = hidden.length;
            while (i--) {
                callback(i, hidden[i], hidden);
            }
            i = spawned.length;
            while (i--) {
                callback(i, spawned[i], spawned);
            }
            checkstack();
        };
        this.deleteOutdated = function () {
            var i = hidden.length,
                att = NotificationType.INCOMING_ATTACK,
                sup = NotificationType.INCOMING_SUPPORT,
                type, now = Timestamp.now();
            that.loop(function (i, elem, arr) {
                if (((type = elem.getType()) === att || type === sup) && elem.getTime() < now) {
                    elem.destroy();
                    arr.remove(i);
                }
            });
        };
        this.setHeightNeededByUnitsBox = function (px) {
            if (px != unitbar_reserved_height) {
                unitbar_reserved_height = px;
                recalcMaxNotifications();
            }
        };
        this.setAdditionalReservedHeight = function (px) {
            if (px != additional_reserved_height) {
                additional_reserved_height = px;
                recalcMaxNotifications();
            }
        };

        function setHiddenCountinLayout() {
            var len = hidden.length;
            $('#hidden_notification_count').html(len).toggle( !! len);
        }

        function checkstack() {
            var newnotify;
            while (spawned.length < max_notifications && (newnotify = hidden.shift())) {
                spawned.push(newnotify);
                newnotify.attachToParent();
            }
            setHiddenCountinLayout();
        }
        this.deleteByTypeAndParamID = function (type, param_id, dont_delete_on_server_side) {
            var i = spawned.length,
                opt;
            this.loop(function (i, elem, arr) {
                opt = elem.getOpt();
                if (opt.type == type && opt.param_id == param_id) {
                    if (!dont_delete_on_server_side) {
                        Layout.ajaxPost('notify', 'delete', {
                            'id': opt.id
                        }, false, function (data) {});
                    }
                    elem.destroy();
                    arr.remove(i);
                }
            });
            setHiddenCountinLayout();
            Layout.updateTitle();
        };

        function recalcMaxNotifications() {
            if (window.location != window.parent.location) {
                return;
            }
            var height = $(window).height(),
                reserved_space = (layout_reserved_height + additional_reserved_height + unitbar_reserved_height),
                notifybox_height = 44,
                x = height - reserved_space,
                i = spawned.length;
            if (x < notifybox_height) {
                x = 1;
            } else {
                x /= notifybox_height;
            }
            x = ~~x;
            if (x < max_notifications) {
                max_notifications = x;
                while (i--) {
                    var notify = spawned[i];
                    notify.despawn();
                    notify = hidden.unshift(notify);
                    spawned.remove(i);
                }
                spawned = [];
                checkstack();
            } else if (x > max_notifications) {
                max_notifications = x;
                checkstack();
            }
        }
        this.del = function (notification) {
            this.loop(function (i, elem, arr) {
                if (notification == elem) {
                    arr.remove(i);
                }
            });
            checkstack();
        };
        this.push = function (notification) {
            var nopt = notification.getOpt(),
                type = NotificationType.AWMESSAGE;
            if (nopt.type === type) {
                var i = spawned.length,
                    opt;
                this.loop(function (i, elem, arr) {
                    opt = elem.getOpt();
                    if (opt.type === type && opt.param_id == nopt.param_id) {
                        if (opt.time < nopt.time) {
                            Layout.ajaxPost('notify', 'delete', {
                                "id": opt.id
                            }, false, function (data) {});
                            elem.destroy();
                            arr.remove(i);
                        } else {
                            notification.remove();
                            return;
                        }
                    }
                });
            }
            hidden.push(notification);
            checkstack();
        };
        recalcMaxNotifications();
        $(window).resize(recalcMaxNotifications);
        $(document).bind('notification.removeAttackNotification', function (evt, notification) {
            that.deleteByTypeAndParamID(NotificationType.INCOMING_ATTACK, notification.param_id, true);
            MenuBubbleMovement.menu_bubble.forceUpdate();
        });
        return this;
    }.call({});

function Notification(options) {
    if (!options) {
        return null;
    }
    var opt = options,
        notification, description, close, icon, is_blocked = false,
        clickable = false,
        that = this;

    function hideDesc() {
        if (clickable) {
            notification.css('cursor', 'auto');
        }
        window.setTimeout(function () {
            if (is_blocked) {
                return;
            }
            description.stop(true, true).fadeOut();
        }, 150);
    }

    function showDesc() {
        if (clickable) {
            notification.css('cursor', 'pointer');
        }
        if (WMap.currently_scrolling) {
            return;
        }
        description.fadeIn();
    }
    this.getType = function () {
        return opt.type;
    };
    this.getTime = function () {
        return opt.time;
    };
    this.getReportId = function () {
        return opt.param_id;
    };
    this.destroy = function () {
        opt.ttl = 0;
        notification.fadeOut(350, 'linear', function () {
            notification.unbind().empty().remove();
        });
    };
    this.age = function () {
        if (!opt.ttl) {
            return false;
        }
        if (opt.ttl <= 0) {
            that.destroy();
        }
        return true;
    };
    var action = null,
        params = [],
        town, str = '',
        t = NotificationType,
        town_id;
    switch (opt.type) {
    case t.NEWREPORT:
        action = Layout.viewReport;
        params.push(opt.param_id);
        break;
    case t.NEWMESSAGE:
        if (opt.html.indexOf('gp_player_link') != -1) {
            action = LayoutModules.LinkHandling.decodeFragment;
            break;
        }
    case t.AWMESSAGE:
        action = Layout.viewMessage;
        params.push(opt.param_id);
        break;
    case t.PLANED_ATTACK:
        action = Layout.viewAttackPlan;
        params.push(opt.param_str);
        break;
    case t.RESOURCETRANSPORT:
        action = Layout.viewResTransport;
        break;
    case t.ALLIANCE_INVITATION:
        action = Layout.wnd.Create;
        params.push(Layout.wnd.TYPE_ALLIANCE);
        break;
    case t.PHOENICIAN_SALESMAN_ARRIVED:
        action = Layout.phoenicianSalesman.open;
        if (ITowns.numTowns() === 1) {
            str = 'The Phoenician merchant has arrived in your city.';
        } else {
            town = ITowns.getTown(opt.param_id);
            str = s('The Phoenician merchant has arrived in %1.', town.name);
        }
        PhoenicianSalesman.showHint(str);
        opt.html = '<a class="notify_subjectlink" href="#">' + str + '</a><br/><span class="small notification_date">' + readableUnixTimestamp(opt.time, 'player_timezone', {
            extended_date: true
        }) + '</span>';
        break;
    case t.PHOENICIAN_SALESMAN_LEAVE:
        if (ITowns.numTowns() === 1) {
            str = 'The Phoenician merchant has left your city.';
        } else {
            town = ITowns.getTown(opt.param_id);
            str = s('The Phoenician merchant has left %1.', town.name);
        }
        opt.html = str + '<br/><span class="small notification_date">' + readableUnixTimestamp(opt.time, 'player_timezone', {
            extended_date: true
        }) + '</span>';
        break;
    case t.BUILDING_FINISHED:
        town_id = ($(opt.html).find("span.notification_building_image").attr("id") || "").split("_");
        town_id = town_id[1] ? town_id[1] : 0;
        action = function () {
            if (town_id && town_id !== Game.townId) {
                Layout.townSwitch(town_id);
            }
            Layout.buildingWindow.open.apply(this, arguments);
        };
        params.push("main");
        break;
    case t.ALL_BUILDING_FINISHED:
        action = Layout.buildingWindow.open;
        params.push('main');
        break;
    case t.SHOW_CAPTCHA:
        var captcha_data = {
            proceed: Layout.hideCaptcha()
        };
        action = Layout.showCaptcha(null, captcha_data);
        params.push('main');
        break;
    default:
        break;
    }
    if (action) {
        clickable = true;
    }
    description = $('<div></div>').addClass('description').html(opt.html);
    close = $('<a></a>').addClass('close');
    icon = $('<div>').addClass('icon');
    notification = $('<div></div>').addClass('notification ' + opt.type + (opt.type == 'planed_attack' ? ' ' + opt.id : '')).append(description.hide()).append(close).append(icon);

    function bindListeners() {
        notification.bind({
            'mouseenter': showDesc,
            'mouseout': hideDesc,
            'click': function (event) {
                if (!Tutorial || !Tutorial.isActive()) {
                    if (action) {
                        event.stopPropagation();
                        action.apply(this, params);
                        Layout.ajaxPost('notify', 'delete', {
                            id: opt.id
                        }, false, function (data) {});
                        NotificationStack.del(that);
                        that.destroy();
                    }
                } else {
                    HumanMessage.error('Sorry, this action isn\'t possible, please finish the tutorial first');
                }
            }
        });
        description.bind({
            'mouseenter': function () {
                is_blocked = true;
            },
            'mouseleave': function () {
                is_blocked = false;
                description.find('.report_html').remove();
            }
        });
        close.bind('click', function (event) {
            event.stopPropagation();
            Layout.ajaxPost('notify', 'delete', {
                id: opt.id
            }, false, function (data) {});
            NotificationStack.del(that);
            that.destroy();
        });
    }
    if (opt.ttl) {
        opt.total_ttl = opt.ttl;
    }
    Layout.updateTitle();
    this.attachToParent = function () {
        bindListeners();
        notification.prependTo(opt.parent).animate({
            top: 0,
            opacity: 1
        }, 1000, 'bounce');
    };
    this.despawn = function () {
        notification.detach().unbind().children().unbind();
    };
    this.getOpt = function () {
        return opt;
    };
}

var LayoutModules = {};
LayoutModules.UnitBar = function UnitBar() {
    var units, expand_g = false,
        expand_s = false,
        gContainer = $('#ground_container'),
        sContainer = $('#sea_container'),
        mContainer = $('#militia_container'),
        sidebar = $('#units_sidebar'),
        msie = jQuery.browser.msie,
        that = this;

    function delegateClick(e) {
        var elm = e.target;
        if (elm.name || (elm = elm.parentNode).name) {
            switch (elm.name) {
            case 'ground':
                expand_g = !expand_g;
                elm.className = expand_g ? elm.className + ' toggled' : elm.className.replace(' toggled', '');
                that.render();
                break;
            case 'sea':
                expand_s = !expand_s;
                elm.className = expand_s ? elm.className + ' toggled' : elm.className.replace(' toggled', '');
                that.render();
                break;
            case 'militia':
                break;
            default:
                MapDuration.selectUnit(elm.name, elm);
            }
        }
        e.preventDefault();
    }
    this.render = function () {
        var d = document,
            gFrag = d.createDocumentFragment(),
            sFrag = d.createDocumentFragment(),
            mFrag = d.createDocumentFragment(),
            i, count, unit, is_ground;
        for (i in units) {
            is_ground = GameData.units[i].capacity === undefined;
            count = units[i];
            if (!isNaN(count) && (count || (is_ground && expand_g && i !== 'militia') || (!is_ground && expand_s))) {
                unit = d.createElement('a');
                unit.className = 'unit ' + (msie ? 'unit_ie' : '') + (MapDuration.selected.indexOf(i) !== -1 ? ' selected' : '');
                unit.name = i;
                unit.href = '#';
                unit.style.backgroundImage = 'url(http://cdn.grepolis.com/images/game/units/' + i + '_40x40.png)';
                unit.innerHTML = '<span class="bold">' + count + '</span>';
                (is_ground ? (i == 'militia' ? mFrag : gFrag) : sFrag).appendChild(unit);
            }
        }
        gContainer.empty();
        sContainer.empty();
        mContainer.empty();
        if (gFrag.childNodes.length) {
            gContainer.append(gFrag);
        }
        if (sFrag.childNodes.length) {
            sContainer.append(sFrag);
        }
        if (!mFrag.childNodes.length) {
            mContainer.parent().hide();
        } else {
            mContainer.append(mFrag).parent().show();
        }
        var totalNeededHeight = sContainer.height() + mContainer.height() + gContainer.height();
        NotificationStack.setHeightNeededByUnitsBox(totalNeededHeight);
        return this;
    };
    this.update = function () {
        units = $.extend(true, {}, ITowns.getTown(Game.townId).units());
        var support = ITowns.getTown(Game.townId).unitsSupport(),
            i;
        for (i in support) {
            units[i] += support[i];
        }
        this.render();
    };
    this.destroy = function () {
        sidebar.unbind();
        MapDuration.clear();
        sidebar.find('a.toggled').removeClass('toggled');
        gContainer.empty().parent().hide();
        sContainer.empty().parent().hide();
        mContainer.empty().parent().hide();
    };
    gContainer.parent().show().next().show();
    sidebar.bind({
        'click': delegateClick
    });
};
LayoutModules.PowerBar = function PowerBar() {
    var sidebar = $('#units_sidebar'),
        container = $('#powers_container'),
        powers, favor, that = this;

    function delegateClick(e) {
        var id = e.target.name;
        if (id) {
            castPower(id);
        }
        e.preventDefault();
    }

    function castPower(power_id) {
        var g = GPEvents.global;
        Layout.ajaxPost('town_info', 'cast', {
            'power': power_id,
            'id': Game.townId,
            'castedFromTownView': true
        }, false, function (data) {
            if (typeof data.finished_at == 'number') {
                g.trigger(g.overviews.power_casted, {
                    'power': power_id,
                    'id': Game.townId,
                    'data': data
                });
            }
            g.trigger(g.overviews.favor_changed, data.bar.favors);
            that.render();
        });
    }
    this.render = function () {
        var d = document,
            fragment = d.createDocumentFragment(),
            p, power, name, i;
        if (isEmpty(powers)) {
            p = d.createElement('div');
            p.innerHTML = 'No effective spells available';
            p.id = 'no_powers';
            fragment.appendChild(p);
        } else {
            for (i in powers) {
                name = powers[i];
                power = GameData.powers[name];
                p = d.createElement('a');
                p.className = 'power_icon ' + name + (power.favor > favor[power.god_id].current ? ' disabled' : '');
                p.name = name;
                p.href = '#';
                fragment.appendChild(p);
            }
        }
        container.empty().append(fragment).parent().show();
        initializePowerPopups(powers);
        var totalNeededHeight = container.height();
        NotificationStack.setHeightNeededByUnitsBox(totalNeededHeight);
    };
    this.destroy = function () {
        sidebar.unbind();
        container.children('a').unbind();
        container.empty().parent().hide();
    };
    this.update = function () {
        powers = ITowns.getTown(Game.townId).powers();
        favor = ITowns.getTown(Game.townId).allGodsFavors();
        this.render();
    };

    function initializePowerPopups(arr) {
        var i;
        for (i in arr) {
            container.find('a.' + arr[i]).setPopup(arr[i]);
        }
    }
    sidebar.bind({
        'click': delegateClick
    });
};
LayoutModules.Resources = function Resources(opt, ref) {
    var that = this,
        jQ = {
            'wood': $('#wood'),
            'iron': $('#iron'),
            'stone': $('#stone'),
            'favor': $('#favor'),
            'population': $('#pop')
        },
        elem = {
            'wood': document.getElementById('wood_count'),
            'iron': document.getElementById('iron_count'),
            'stone': document.getElementById('stone_count'),
            'favor': document.getElementById('favor_text'),
            'population': document.getElementById('pop_current')
        },
        timers = {};
    jQ.wood.click(function () {
        Layout.buildingWindow.open('lumber');
    });
    jQ.stone.click(function () {
        Layout.buildingWindow.open('stoner');
    });
    jQ.iron.click(function () {
        Layout.buildingWindow.open('ironer');
    });
    jQ.population.click(function () {
        Layout.buildingWindow.open('farm');
    });
    jQ.favor.click(function () {
        Layout.buildingWindow.open('temple');
    });
    this.setResources = function (res, id) {
        id = id || Game.townId;
        ITowns.setResources(res, id);
        if (id === Game.townId) {
            that.renderResources();
        }
        return this;
    };
    this.setResourcesContraints = function (resc, id) {
        var res_types = {
            'stone': 'stone',
            'iron': 'iron',
            'wood': 'wood'
        },
            html = '',
            type;
        id = id || Game.townId;
        ITowns.setResourcesConstraints(resc, id);
        if (id === Game.townId) {
            for (type in res_types) {
                if (res_types.hasOwnProperty(type)) {
                    if (type === resc.plenty) {
                        html = '<span class="res_plenty">';
                    } else if (type === resc.rare) {
                        html = '<span class="res_rare">';
                    }
                    jQ[type].find('span.' + type + '_img').html(html);
                    html = '';
                }
            }
        }
        return this;
    };
    this.setProduction = function (prod, id) {
        id = id || Game.townId;
        ITowns.setProduction(prod, id);
        resetResourceRemainder();
        return this;
    };
    this.setGod = function (god, id) {
        id = id || Game.townId;
        ITowns.setGod(god, id);
        return this;
    };

    function updateResourceCounter(town_id) {
        ITowns.updateResourceCounter(town_id || Game.townId);
    }

    function resetResourceRemainder(town_id) {
        var town = ITowns.getTown(town_id || Game.townId);
        town.resourcesRemainders({
            'wood': 0,
            'stone': 0,
            'iron': 0,
            'favor': 0
        });
    }
    this.initializeResourcesProductionCounter = function () {
        var town = ITowns.getTown(Game.townId),
            timeout = 12E4,
            i, production = town.production();
        for (i in production) {
            timeout = bound((36E5 / production[i]), 1E3, timeout);
        }
        timeout = timeout >> 1;
        window.clearInterval(timers.prod);
        timers.prod = window.setInterval(function () {
            updateResourceCounter();
        }, timeout);
        return this;
    };
    this.updateProductionPopups = function () {
        var town = ITowns.getTown(Game.townId);
        var res_constraints = town.resourcesConstraints();
        var popup_data = {};
        $.extend(popup_data, town.production());
        popup_data.storage_volume = town.storage();
        popup_data.resource_rare = res_constraints.rare;
        popup_data.resource_plenty = res_constraints.plenty;
        PopupFactory.updateProductionPopup('wood', popup_data);
        PopupFactory.updateProductionPopup('stone', popup_data);
        PopupFactory.updateProductionPopup('iron', popup_data);
        jQ.wood.setPopup('wood_production');
        jQ.iron.setPopup('iron_production');
        jQ.stone.setPopup('stone_production');
    };
    this.renderResources = function (event, diff) {
        var current_town = ITowns.getTown(Game.townId),
            storage = current_town.storage(),
            res = current_town.resources(),
            fullres = 'resources_full',
            conf = opt.resProgressConfig,
            q, offset, h1, h2, orientation, i, val, threshold = 10,
            fx, callback = function () {
                fx.remove();
            };
        if (diff) {
            for (i in res) {
                val = diff[i];
                if (Math.abs(val) > threshold && Game.gfx_level) {
                    fx = $('<span class="' + (diff[i] > 0 ? 'add ' : 'sub ') + i + '_fx res_fx">' + diff[i] + '</span>');
                    if (elem[i]) {
                        fx.appendTo(elem[i].parentNode).animate({
                            'fontSize': 30,
                            'opacity': 0
                        }, 3E3, 'swing', callback);
                    }
                }
            }
        }
        for (i in res) {
            if (elem[i]) {
                if (i === 'favor') {
                    orientation = conf.a.orientation;
                    h1 = conf.a.size;
                    h2 = conf.a.segments * h1;
                    q = res[i] / opt.maxFavor;
                } else {
                    orientation = conf.b.orientation;
                    h1 = conf.b.size;
                    h2 = conf.b.segments * h1;
                    q = res[i] / storage;
                }
                offset = q * h2;
                offset -= offset % h1;
                elem[i].innerHTML = res[i];
                jQ[i].css({
                    backgroundPosition: orientation === 'x' ? '-' + (h2 - offset) + 'px 0' : '0 -' + offset + 'px'
                });
                if ((i === 'favor' && res[i] === opt.maxFavor) || current_town.isMax(i)) {
                    jQ[i].addClass(fullres);
                } else {
                    jQ[i].removeClass(fullres);
                }
            }
        }
    };
    GPEvents.global.bind(GPEvents.global.town.resources_update, that.renderResources);
};
LayoutModules.TownNameList = function TownNameList() {
    var timers = {},
        that = this,
        listDropdownElement = $('#town_list'),
        townNameElement = $('#town_name'),
        saveTownNameElement = $('#save_town_name'),
        townLinkElement = $('#town_name_link');

    function bindTownNameEvents() {
        townLinkElement.bind({
            'dblclick.tlist': function () {
                that.changeTownName(this);
            },
            'click.tlist': function () {
                if (timers.toggleTownList) {
                    return;
                }
                timers.toggleTownList = window.setTimeout(function () {
                    that.toggleTownList();
                }, 250);
            }
        });
    }
    this.changeTownName = function (elem) {
        that.lock_save_town_name = false;
        window.clearTimeout(timers.toggleTownList);
        delete timers.toggleTownList;
        townNameElement.unbind('click.tlist').bind('keydown', function (e) {
            if (e.keyCode === 27) {
                cancelNameChange();
            } else if (e.keyCode !== 13) {
                that.lock_save_town_name = false;
            }
        });
        saveTownNameElement.click(saveTownName);
        townLinkElement.hide().next().fadeIn().find('input').focus().parents('form').bind('submit', saveTownName);
    };

    function cancelNameChange() {
        townLinkElement.show().next().hide().parent().unbind('submit keydown');
        bindTownNameEvents();
    }

    function saveTownName(ev) {
        var inp = $('#town_name_input'),
            town_name = $.trim(inp.val());
        ev.stopPropagation();
        if (town_name === $.trim(townLinkElement.text())) {
            return cancelNameChange();
        }
        if (that.lock_save_town_name !== undefined && that.lock_save_town_name) {
            return false;
        }
        that.lock_save_town_name = true;
        Layout.ajaxPost('town_index', 'set_town_name', {
            town_name: town_name
        }, true, function (data) {
            if (data.success) {
                townLinkElement.text(data.town_name).show().next().hide().parent().unbind('submit keydown');
                bindTownNameEvents();
                if (ITowns.setName(town_name, Game.townId)) {
                    that.createTownListDropdown(true);
                }
                Game.townName = data.town_name;
                inp.val(data.town_name);
                Layout.updateTitle();
            }
        });
        return false;
    }
    this.toggleTownList = function () {
        window.clearTimeout(timers.toggleTownList);
        delete timers.toggleTownList;
        if (this.town_list_toggle) {
            return;
        }
        if (listDropdownElement.is(':visible')) {
            listDropdownElement.hide().empty();
        } else {
            this.town_list_toggle = true;
            listDropdownElement.detach();
            this.createTownListDropdown();
            $('a.group_link').mousePopup(new MousePopup('City group. Manage your cities in groups to get a better overview.'));
            delete this.town_list_toggle;
        }
    };
    this.createTownListDropdown = function (flag) {
        var lists, i, groups = ITowns.getTownGroups();
        flag = flag || false;
        if (flag && !listDropdownElement.is(':visible')) {
            return;
        }
        listDropdownElement.empty().html(tmpl2(GameData.TownListTemplate, {
            'groups': groups
        })).appendTo('#box').show();
        lists = listDropdownElement.find('ul');
        lists.sortable({
            handle: 'span.handle',
            connectWith: "ul.town_group",
            stop: ITowns.getTownGroupsStopSortCallback(),
            helper: 'clone'
        });

        function toggle(saved_settings) {
            var list = this.parent().next(),
                id = list[0].id.match(/\d+/g) || 0,
                ltoggle = groups[id].toggled,
                speed = saved_settings ? 0 : 300;
            if (saved_settings || ltoggle === undefined ? ltoggle : !ltoggle) {
                list.slideDown(speed);
            } else {
                list.slideUp(speed);
            }
            groups[id].toggled = saved_settings || ltoggle === undefined ? (groups[id].toggled ? true : false) : !ltoggle;
            this.toggleClass('toggled', !groups[id].toggled);
        }
        i = lists.length;
        while (listDropdownElement.height() > 200 && i > 1) {
            toggle.call($(lists.prev().children('a.toggle')[--i]), true);
        }
        listDropdownElement.unbind().bind({
            click: function (ev) {
                var elem = ev.target,
                    id;
                switch (elem.className.split(' ')[0]) {
                case 'town_link':
                    id = elem.parentNode.id.match(/\d+|null/g);
                    ITowns.townGroupsTownSwitch(id[1], id[0]);
                    that.toggleTownList();
                    break;
                case 'toggle':
                    toggle.call($(elem));
                    break;
                case 'delete':
                    id = elem.parentNode.id.match(/\d+|null/g);
                    ITowns.townGroupsRemoveFromGroup(id[1], id[0]);
                    break;
                case 'group_link':
                    id = $(elem).parent().next()[0].id.match(/\d+|null/);
                    if (~~ITowns.getActiveTownGroup().id != id[0]) {
                        ITowns.setActiveTownGroup(id[0]);
                    } else {
                        ITowns.setActiveTownGroup(null);
                    }
                    listDropdownElement.find('a.group_link').mousePopup(new MousePopup('City group. Manage your cities in groups to get a better overview.'));
                    break;
                default:
                    return false;
                }
            }
        });
        ITowns.setTownGroupsDropdown(listDropdownElement);
    };
    bindTownNameEvents();
};
LayoutModules.LinkHandling = function () {
    var that = this;
    this.decodeFragment = function (e) {
        var link = $(e.target).closest('a'),
            href = link.attr('href'),
            c_name = link.attr('class'),
            fragment, gptown, gpplayer, gpisland;
        if (!href) {
            if (!e.target.parentNode.href) {
                return null;
            } else {
                href = e.target.parentNode.href;
            }
        }
        gptown = c_name === 'gp_town_link';
        gpplayer = c_name === 'gp_player_link';
        gpisland = c_name === 'gp_island_link';
        fragment = href.split('#').reverse()[0].split('.');
        if (!fragment[0] || !fragment.length || e.button !== 0) {
            return null;
        }
        if ((gptown | gpplayer | gpisland)) {
            e.preventDefault();
            return that.checkTownOrPlayerLink(e, fragment[0], (gptown << 2) + (gpplayer << 1) + gpisland);
        } else if (that.Menu.followLink(Layout, fragment)) {
            e.preventDefault();
        }
    };
    this.keyPress = function (e) {
        var keyCode = e.keyCode;
        if (e.target && e.target.tagName.match(/INPUT|TEXTAREA|SELECT|OPTION/)) {
            return;
        }
        switch (keyCode) {
        case 37:
            that.Menu.followLink(Layout, 'prev_city');
            break;
        case 39:
            that.Menu.followLink(Layout, 'next_city');
            break;
        }
    };
};
LayoutModules.LinkHandling.prototype.checkTownOrPlayerLink = function (e, fragment, bitmask) {
    var data, gptown = bitmask & 4,
        gpisland = bitmask & 1;
    data = $.parseJSON(atob(fragment));
    if (gptown) {
        Layout.contextMenu(e, 'determine', data);
    } else if (gpisland) {
        Layout.contextMenu(e, 'island', data);
    } else {
        Layout.playerProfile.open.call(Layout, data.name, data.id);
    }
    return data;
};
LayoutModules.LinkHandling.prototype.Menu = {
    followLink: function (context, type) {
        var namespace = type[1],
            params = {};
        switch (type[0]) {
        case 'townindex':
            context.townIndex.open(Game.townId, Game.townName);
            return true;
        case 'messages':
            context.wnd.Create(Layout.wnd.TYPE_MESSAGE, 'Messages');
            return true;
        case 'reports':
            context.wnd.Create(Layout.wnd.TYPE_REPORT, 'Reports');
            return true;
        case 'alliance':
            context.wnd.Create(Layout.wnd.TYPE_ALLIANCE);
            return true;
        case 'allianceforum':
            context.allianceForum.open();
            return true;
        case 'attackplaner':
            context.wnd.Create(Layout.wnd.TYPE_ATTACK_PLANER, 'Attack planner');
            return true;
        case 'settings':
            context.wnd.Create(Layout.wnd.TYPE_PLAYER_SETTINGS, 'Settings');
            return true;
        case 'profile':
            context.wnd.Create(Layout.wnd.TYPE_PLAYER_PROFILE_EDIT, 'Edit profile');
            return true;
        case 'activate':
            if (namespace) {
                Layout.ajaxGet('premium_features', 'enough_gold_for_advisor', {
                    'advisor': namespace
                }, true, function (data) {
                    var text, wnd;
                    if (data.enough) {
                        text = GameData.texts[namespace + '_confirm'];
                        Layout.showConfirmDialog(text.title, text.content, function () {
                            WndHandlerPremium.prototype.extendFeature(null, namespace, false);
                        }, null, 'onCancel', null, 'onCheck', text.check);
                    } else {
                        wnd = GPWindowMgr.Create(GPWindowMgr.TYPE_DIALOG, "Replenish gold");
                        wnd.getHandler().onRcvData(data);
                        wnd.setWidth(450);
                    }
                });
            }
            return true;
        case 'premium':
            if (namespace) {
                params = {
                    sub_content: 'premium_overview',
                    sub_tab: namespace
                };
            }
            context.wnd.Create(Layout.wnd.TYPE_PREMIUM, 'Premium', params);
            return true;
        case 'premium_buy_gold':
            context.wnd.Create(Layout.wnd.TYPE_PREMIUM, 'Premium', {
                sub_content: 'buy_gold'
            });
            return true;
        case 'chat':
            context.wnd.Create(Layout.wnd.TYPE_CHAT, 'Chat');
            return true;
        case 'logout':
            context.ajaxPost('player', 'logout', {}, true, function () {});
            return true;
        case 'city_index':
            context.townIndex.open(Game.townId, Game.townName);
            return true;
        case 'next_city':
            context.townSwitch(ITowns.getNextTownId(Game.townId));
            return true;
        case 'prev_city':
            context.townSwitch(ITowns.getPrevTownId(Game.townId));
            return true;
        case 'memo':
            context.showMemoWindow();
            return true;
        case 'invite_friends':
            Kontagent.trackEvent({
                userId: Game.uid,
                eventName: 'invite_friends_from_menu',
                subtype1: Game.market_id,
                subtype2: Game.world_id,
                subtype3: 'invitation'
            }, function () {});
            context.wnd.Create(Layout.wnd.TYPE_INVITE_FRIENDS, 'Invite players');
            return true;
        default:
            return false;
        }
    }
};

function GPLayoutB(opt) {
    var options = opt,
        that = this,
        jQElem = {
            'notificationArea': $('#notification_area'),
            'delAllNot': $('#delete_all_notifications'),
            'box': $('#box'),
            'document': $(document),
            'mainArea': $('#main_area'),
            'quickbar': $('#toolbar'),
            'tLink': $('#town_name_link'),
            'favor': $('#favor'),
            'godMini': $('#god_mini'),
            'overviewMenu': $('#overviews_link_hover_menu')
        },
        elem = {
            'ajaxLoader': null,
            'serverTime': document.getElementById('server_time')
        },
        nStack = NotificationStack,
        notification_last_requested_id = 0,
        notification_next_fetch_at = 0,
        client_server_time_diff = Timestamp.clientServerDiff(),
        timers = {},
        bar_request_bitmask = 0,
        casted_powers_on_own_town_timeouts = [],
        linkHandling = new LayoutModules.LinkHandling();
    this.townNameList = new LayoutModules.TownNameList();
    opt.resProgressConfig = {
        a: {
            orientation: 'x',
            size: 78,
            segments: 22
        },
        b: {
            orientation: 'y',
            size: 42,
            segments: 25
        }
    };
    this.resources = new LayoutModules.Resources(opt);
    this.unitBar = new LayoutModules.UnitBar();

    function initialize(onInit) {
        bindGlobalEvents();
        bindEvents();
        $('#switch_powers').mousePopup(new MousePopup('Spells'));
        $('#switch_units').mousePopup(new MousePopup('Units'));
        $('#switch_troops').mousePopup(new MousePopup('Recruitments'));
        $('#switch_trade').mousePopup(new MousePopup('Trade'));
        $('#switch_movement').mousePopup(new MousePopup('Troop movements'));
        GPWindow.prototype.minimize = function () {
            var typeinfo = GPWindowMgr.getTypeInfo(this.type),
                wrapper, a, is_id = typeinfo.maxconcurrent === 1,
                selector = typeinfo.tselector,
                that = this;
            if (selector.match('TODO')) {
                throw 'No selector found for this type: ' + this.type;
            }
            a = is_id ? $(selector) : $('min_' + this.getID());
            if (!a.length) {
                wrapper = $('<div class="icon_bg"></div>');
                a = $('<a href="#"></a>');
                a.attr({
                    'id': is_id ? selector.slice(1) : 'min_' + this.getID(),
                    'class': is_id ? '' : selector.slice(1),
                    'title': this.getTitle()
                }).appendTo(wrapper);
                wrapper.hide().addClass('temp_elem').appendTo('#menu').fadeIn();
            }
            a.unbind().click(function (e) {
                that.toTop(e);
            }).parents('div.icon_bg').addClass('minimized');
            this.getJQElement().parent().hide();
        };
        GPWindow.prototype.maximize = function (e) {
            var typeinfo = GPWindowMgr.getTypeInfo(this.type),
                is_id = typeinfo.maxconcurrent === 1,
                selector = typeinfo.tselector,
                a = is_id ? $(selector) : $('#min_' + this.getID()),
                wrapper = a.parents('div.icon_bg'),
                is_temp = wrapper.hasClass('temp_elem');
            if (e) {
                e.stopPropagation();
                e.preventDefault();
            }
            a.unbind();
            if (is_temp) {
                wrapper.fadeOut(500, function () {
                    wrapper.remove();
                    wrapper = null;
                });
            }
            wrapper.removeClass('minimized');
            this.getJQElement().parent().show();
        };
        Quickbar.refresh = function (data) {
            var toolbar = $('#toolbar');
            if (data.quickbar) {
                if (toolbar.length) {
                    toolbar.remove();
                }
                $('#bottombar').append(data.quickbar);
                Quickbar.layoutInitializeQuickbar();
            } else {
                toolbar.remove();
            }
        };
    }

    function createAjaxLoader() {
        var img = document.createElement('img');
        img.id = 'ajax_loader';
        img.src = "http://cdn.grepolis.com/images/game/ajax-loader.gif";
        img.alt = '';
        img.style.top = ((jQElem.box.height() >> 1) - 29) + 'px';
        img.style.left = ((jQElem.box.width() >> 1) - 29) + 'px';
        jQElem.box.append(img);
        return img;
    }

    function bindGlobalEvents() {
        jQElem.document.bind('notification.doRefetchBar', refetchBar);
        $(window).focus(checkCookieTxRxData);
    }

    function checkCookieTxRxData() {
        var data = $.cookie('txrx_lnk'),
            menu;
        if (!data) {
            return;
        }
        $.cookie('txrx_lnk', '{}', {
            expires: -1,
            path: '/'
        });
        data = $.parseJSON(data);
        if (data.com_type && data.com_type == 1) {
            menu = new ContextMenu({
                'type': 'determine',
                'town': data
            });
            $(menu).find('#' + data.cmd).trigger('mousedown').hide().unbind().remove();
        } else if (data.com_type && data.com_type == 2) {
            ReportViewer.dates[data.report_id] = data.report_data;
            if (data.cmd == "all") {
                ReportViewer.insertAllUnitsToSimulator(true, true, data.report_id);
            } else {
                ReportViewer.insertRemainingUnitsToSimulator(true, true, data.report_id);
            }
        }
    }

    function bindEvents() {
        jQElem.document.bind({
            'click.tname': linkHandling.decodeFragment,
            'keyup': linkHandling.keyPress
        });
        that.bindPremiumStuff();
        jQElem.delAllNot.click(deleteAllNotifications);
        $('#close_all_windows').click(that.wnd.closeAll);
        $('#incomming_attack_count').bind({
            'click': function (ev) {
                if (!$('#city_overview_link').hasClass('inactive')) {
                    Overviews.openOverview('command_overview');
                } else {
                    Layout.wnd.Create(Layout.wnd.TYPE_PREMIUM, 'Premium', {
                        sub_content: 'premium_overview'
                    }, Game.townId);
                }
            },
            'mouseenter': function () {
                $(this).css('cursor', 'pointer');
            },
            'mouseleave': function () {
                $(this).css('cursor', 'auto');
            }
        });
        $('#incomming_attack_count').mousePopup(new MousePopup('Arriving attacks'));
        $('#sidebar_switch').click(switchSidebar);
        var popup_div = $('#popup_div');
        popup_div.unbind('.popup').bind({
            'mouseleave.popup': function () {
                $('#popup_div').hide().clearQueue();
            }
        });
    }

    function switchSidebar(e) {
        e.preventDefault();
        var target = e.target,
            href = target.href,
            type;
        if (href) {
            type = href.split(/#/).reverse()[0];
            $(target).addClass('selected').siblings().removeClass('selected');
        } else {
            return false;
        }
        that.unitBar.destroy();
        switch (type) {
        case 'powers':
            that.unitBar = new LayoutModules.PowerBar();
            break;
        case 'units':
            that.unitBar = new LayoutModules.UnitBar();
            break;
        case 'troops':
            that.unitBar = MenuBubbleOrders.menu_bubble;
            break;
        case 'trade':
            that.unitBar = MenuBubbleTrade.menu_bubble;
            break;
        case 'movement':
            that.unitBar = MenuBubbleMovement.menu_bubble;
            break;
        }
        that.unitBar.update();
        return true;
    }
    this.bindPremiumStuff = function (type) {
        if (type) {
            jQElem.overviewMenu = [];
        }
        if (!jQElem.overviewMenu.length) {
            jQElem.overviewMenu = $('#overviews_link_hover_menu');
        }
        $('#city_overview_link').unbind().bind({
            'click': function () {
                switch (type || this.className) {
                case 'inactive':
                    Layout.wnd.Create(Layout.wnd.TYPE_PREMIUM, 'Premium', {
                        sub_content: 'premium_overview'
                    }, Game.townId);
                    break;
                case 'curator':
                    Overviews.openOverview('trade_overview');
                    break;
                case 'captain':
                    Layout.wnd.Create(Layout.wnd.TYPE_FARM_TOWN_OVERVIEWS, 'Farming villages', {});
                    break;
                }
            },
            'mouseenter': function (ev) {
                if (jQElem.overviewMenu) {
                    jQElem.overviewMenu.appendTo('body').show();
                }
            },
            'mouseleave': function (ev) {
                if (!jQElem.overviewMenu.length) {
                    return;
                }
                var menu = jQElem.overviewMenu;
                if (menu.hover()) {
                    menu.hover(function () {}, function () {
                        menu.hide();
                    });
                }
            }
        });
        jQElem.overviewMenu.click(function (e) {
            var name = e.target.name;
            switch (name) {
            case 'town_group_overview':
                Overviews.openOverview(name, name + 's');
                break;
            case 'farm_town_overview':
                Layout.wnd.Create(Layout.wnd.TYPE_FARM_TOWN_OVERVIEWS, 'Farming villages', {});
                break;
            default:
                Overviews.openOverview(name);
            }
        });
        this.bindMenuBubbleClickHandler('troops');
        this.bindMenuBubbleClickHandler('movement');
        this.bindMenuBubbleClickHandler('trade');
    };

    function updateServerTime() {
        var servertime, current_server_time = (new Date()).getTime() - client_server_time_diff * 1E3;
        servertime = new Date(current_server_time + Timestamp.serverGMTOffset() * 1E3);
        elem.serverTime.innerHTML = getHumanReadableTimeDate(servertime);
        if ($('#server_time_popup').length) {
            servertime = new Date(current_server_time + Timestamp.localeGMTOffset() * 1E3);
            $('#server_time_popup').html(getHumanReadableTimeDate(servertime));
        }
    }
    this.showMemoWindow = function () {
        if (!$('#city_overview_link').hasClass('inactive')) {
            Layout.ajaxGet('player_memo', 'load_memo_content', {}, false, function (data) {
                var wnd_memo = Layout.wnd.Create(Layout.wnd.TYPE_MEMO, '', {
                    data: data
                });
                if (wnd_memo) {
                    wnd_memo.sendMessage('setData', data);
                }
            });
        } else {
            Layout.wnd.Create(Layout.wnd.TYPE_PREMIUM, 'Premium', {
                sub_content: 'premium_overview'
            }, Game.townId);
        }
    };
    GPEvents.global.bind(GPEvents.global.unit_orders.unit_finished, handleUnitOrderFinished);

    function handleUnitOrderFinished(event, data) {
        var town = ITowns.getTown(Game.townId);
        var units = {};
        units[data.unit_id] = 1;
        town.addUnits(units);
        that.unitBar.update();
    }
    GPEvents.global.bind(GPEvents.global.system_notifications.conquest_info, handleConquerAndColonizationUpdate);
    GPEvents.global.bind(GPEvents.global.system_notifications.uninhabited_place_colonized, handleConquerAndColonizationUpdate);

    function handleConquerAndColonizationUpdate(event, notification) {
        var i, towns, n;
        if (jQElem.tListDropdown.is(':visible')) {
            jQElem.tListDropdown.hide().empty();
        }
        switch (event.type) {
        case GPEvents.global.system_notifications.conquest_info:
            if (notification.param_id == 0) {
                towns = ITowns.getTowns();
                n = 0;
                for (i in towns) {
                    if (!towns.hasOwnProperty(i)) {
                        continue;
                    }
                    n++;
                }
                if (n <= 1) {
                    Layout.ajaxPost('player', 'logout', {}, true, function (data) {});
                    return;
                } else if (notification.param_str.town_id == Game.townId) {
                    Layout.nextTown();
                }
            }
            break;
        case GPEvents.global.system_notifications.uninhabited_place_colonized:
            break;
        default:
            break;
        }
        WMap.pollForMapChunksUpdate();
        ITowns.refetch();
        var w = Layout.wnd.getOpenFirst(Layout.wnd.TYPE_TOWN_OVERVIEWS);
        if (w) {
            w.getHandler().sendMessage('onReload');
        }
    }
    this.updateTitle = function () {
        var len = nStack.length();
        document.title = 'Grepolis' + (len ? ' (' + len + ')' : '') + ' - ' + Game.townName;
    };
    this.townSwitch = function (townID) {
        if (townID == Game.townId) {
            return;
        }
        var new_town = ITowns.getTown(townID);
        Game.townId = townID;
        Game.townName = new_town.name;
        $('#town_name_input').val(Game.townName);
        jQElem.tLink.text(Game.townName);
        that.updateTitle();
        $('#town_name_link').removeClass('powers_active').parent().find('div.town_powers').children().remove();
        var building_wnd = that.buildingWindow.getWnd();
        if (building_wnd) {
            building_wnd.sendMessage('refreshCurrentBuilding');
        }
        var townindex_wnd = that.townIndex.getWnd();
        if (townindex_wnd) {
            that.townIndex.open(Game.townId, Game.townName);
        }
        WMap.setCurrentTown(Game.townId, new_town.island_x, new_town.island_y);
        that.ajax('index', 'switch_town', {}, true, function (data) {
            if (!hasValidTownToken(data)) {
                return;
            }
            if (data.bar) {
                that.updateBar(data.bar);
            }
            jQElem.document.trigger(GPEvents.global.town.town_switch);
            if (new_town.has_conqueror) {
                that.conquestWindow.open(Game.townId);
            } else {
                that.conquestWindow.close();
            }
        });
    };
    this.bindMenuBubbleClickHandler = function (name, obj) {
        var has_premium = !$('#city_overview_link').hasClass('inactive');
        if (!obj) {
            obj = $('#icon_' + name);
        }
        obj.unbind('click');
        if (has_premium) {
            var overview_actions = {
                'troops': 'recruit_overview',
                'movement': 'command_overview',
                'trade': 'trade_overview'
            };
            obj.click(function () {
                Overviews.openOverview(overview_actions[name]);
            });
        } else {
            obj.click(function () {
                Layout.wnd.Create(Layout.wnd.TYPE_PREMIUM, 'Premium', {
                    sub_content: 'premium_overview'
                }, Game.townId);
            });
        }
    };
    var g_MaintenanceBlockActive = false;
    this.isMaintenanceBlocked = function () {
        return g_MaintenanceBlockActive;
    };
    this.blockForMaintenance = function (url) {
        if (g_MaintenanceBlockActive) {
            return;
        }
        g_MaintenanceBlockActive = true;
        jQuery.blocker({
            'html': $("<div><div style=\"text-align: center;\"><b>" + 'Maintenance mode' + "</b></div>" + "<div style=\"text-align: center; margin-top: 15px;\">" + 'Sorry, we\'re currently doing maintenance. You were automatically logged out!' + "</div><br><br></div>").append(button('Back to the login', {
                style: "float: right;",
                onClick: "jQuery.blocker.unblock();",
                href: "#"
            })),
            'caching': false,
            'callback': function () {
                window.location = url;
            }
        });
    };
    var tutorial_reshighlight_arrow = null;
    this.tutorialResourceHighlight = function (on_population) {
        if (on_population === true) {
            tutorial_reshighlight_arrow = Tutorial.makeArrow('#res', 'e', {
                x: 230,
                y: 20
            });
            tutorial_reshighlight_arrow.bounce();
        } else {
            tutorial_reshighlight_arrow = Tutorial.makeArrow('#res', 'w', {
                x: -170,
                y: 20
            });
            tutorial_reshighlight_arrow.bounce();
        }
    };
    this.stopTutorialResourceHighlight = function () {
        if (tutorial_reshighlight_arrow != null) {
            tutorial_reshighlight_arrow.remove();
            tutorial_reshighlight_arrow = null;
        }
    };
    var tutorial_cityindex_arrows = [];
    this.tutorialShowCityIndexArrows = function () {
        tutorial_cityindex_arrows[0] = Tutorial.makeArrow('#city_index', 'e', {
            x: -5,
            y: 0
        });
        tutorial_cityindex_arrows[0].bounce();
    };
    this.stopTutorialCityIndexArrows = function () {
        if (tutorial_cityindex_arrows[0] != null) {
            tutorial_cityindex_arrows[0].remove();
            tutorial_cityindex_arrows[0] = null;
        }
    };
    var tutorial_menububblehighlight_arrow = null;
    this.tutorialMenuBubbleHighlight = function () {
        tutorial_menububblehighlight_arrow = Tutorial.makeArrow('#switch_movement', 'w', {
            x: -5,
            y: 0
        });
        tutorial_menububblehighlight_arrow.bounce();
    };
    this.stopTutorialMenuBubbleHighlight = function () {
        if (tutorial_menububblehighlight_arrow != null) {
            tutorial_menububblehighlight_arrow.remove();
            tutorial_menububblehighlight_arrow = null;
        }
    };
    this.getBarBitMask = function () {
        return {
            LAYOUT_BAR_RESOURCES: 2,
            LAYOUT_BAR_FAVOR: 4,
            LAYOUT_BAR_GOD: 8,
            LAYOUT_BAR_POPULATION: 16,
            LAYOUT_BAR_REPORT_STATUS: 32,
            LAYOUT_BAR_NEW_FARM_TOWN_ATTACK_REPORT: 64,
            LAYOUT_BAR_HAS_CONQUERER: 128,
            LAYOUT_BAR_BUILDING_ORDERS: 256,
            LAYOUT_BAR_CASTED_POWERS: 512,
            LAYOUT_BAR_UNITS: 1024,
            LAYOUT_BAR_FAVOR_GODS_OVERVIEW: 2048,
            LAYOUT_BAR_MENU_BUBBLES: 4096,
            LAYOUT_BAR_PRODUCTION: 8192,
            LAYOUT_BAR_RANKING: 16384,
            LAYOUT_BAR_GIFT: 65536,
            LAYOUT_BAR_FULL: 131071
        };
    };

    function refetchBar(event, notification) {
        if (notification.param_id == Game.townId) {
            Layout.requestBarSubstreamWithNextNotificationTransmission(notification.param_str);
            Layout.resetNotificationRequestTimeout(3000);
            Layout.updateProductionPopups();
        }
    }
    this.requestBarSubstreamWithNextNotificationTransmission = function (bar_bitmask) {
        bar_request_bitmask = bar_bitmask >= 0 ? bar_bitmask : 0;
    };
    this.getLastRequestedNotificationId = function () {
        return notification_last_requested_id;
    };
    this.requestNotifications = function (_no_sysmsg) {
        if (!_no_sysmsg) {
            _no_sysmsg = false;
        }
        notification_next_fetch_at = 0;
        this.ajaxGet('notify', 'fetch', {
            bar_mask: bar_request_bitmask,
            'no_sysmsg': _no_sysmsg
        }, false, function () {});
        bar_request_bitmask = 0;
    };
    this.resetNotificationRequestTimeout = function (next_fetch_in) {
        next_fetch_in = next_fetch_in || 10;
        var now = Timestamp.now('c');
        if (notification_next_fetch_at < now || (notification_next_fetch_at > now && notification_next_fetch_at > now + Math.ceil(next_fetch_in * 0.001))) {
            notification_next_fetch_at = now + Math.ceil(next_fetch_in * 0.001);
            if (timers.notificationrequesttimeout) {
                window.clearTimeout(timers.notificationrequesttimeout);
            }
            timers.notificationrequesttimeout = window.setTimeout(function () {
                Layout.requestNotifications();
            }, next_fetch_in);
        }
    };
    this.recvNotifyData = function (data) {
        var notification, i = data.notifications.length,
            id, showOnLogin;
        deleteOutdatedNotifications();
        if (data.incomming_attack_count !== undefined) {
            that.setIncommingAttackCount(~~data.incomming_attack_count);
        }
        data.notifications.reverse();
        while (i--) {
            notification = data.notifications[i];
            id = parseInt(notification.id, 10);
            if (notification_last_requested_id < id) {
                Game.notification_last_requested_id = notification_last_requested_id = id;
            }
            switch (notification.type) {
            case NotificationType.SHOW_CURATOR_POPUP:
                that.handleCuratorPopupNotification(notification);
                break;
            case NotificationType.SYSTEMMESSAGE:
                that.handleSystemNotification(notification);
                break;
            case NotificationType.QUEST:
                Tutorial.handleNotification(notification);
                break;
            case NotificationType.INCOMING_ATTACK:
                if (that.player_hint_settings.militia_hint && !that.militia_hint_shown) {
                    showOnLogin = {
                        title: 'Warning, you\'re being attacked!',
                        text: '<p>' + '<span class="index_unit" style="float:left;"><img src="http://cdn.grepolis.com/images/game/units/militia_40x40.png" alt="militia" /></span>' + 'Arm your city residents and call in the militia to defend yourself better. You can find further information at the farm.' + '</p><hr /><a class="confirm" href="#confirm"></a>',
                        type: 'hint.militia'
                    };
                    (GPWindowMgr.Create(GPWindowMgr.TYPE_SHOW_ON_LOGIN) || GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_SHOW_ON_LOGIN)).getHandler().onRcvData(showOnLogin);
                    that.militia_hint_shown = true;
                }
                that.notify(notification.id, notification.type, notification.subject + '<br/><span class="small">' + 'Arrival:' + readableUnixTimestamp(notification.time, 'player_timezone', {
                    extended_date: true
                }) + '</span>', notification.time, notification.param_id, notification.param_str);
                break;
            case NotificationType.INCOMING_SUPPORT:
                that.notify(notification.id, notification.type, notification.subject + '<br/><span class="small">' + 'Arrival:' + readableUnixTimestamp(notification.time, 'player_timezone', {
                    extended_date: true
                }) + '</span>', notification.time, notification.param_id, notification.param_str);
                break;
            default:
                if (notification.type == NotificationType.PHOENICIAN_SALESMAN_ARRIVED || notification.type == NotificationType.PHOENICIAN_SALESMAN_LEAVE) {
                    var town = ITowns.getTown(notification.param_id);
                    if (town == undefined) {
                        Layout.ajaxPost('notify', 'delete', {
                            id: notification.id
                        }, false);
                        continue;
                    }
                }
                notification.subject = '<a class="notify_subjectlink" href="#">' + notification.subject + '</a>';
                that.notify(notification.id, notification.type, notification.subject + '<span class="small notification_date">' + readableUnixTimestamp(notification.time, 'player_timezone', {
                    extended_date: true
                }) + '</span>', notification.time, notification.param_id, notification.param_str);
                break;
            }
        }
        var next_fetch_in;
        if (data.next_fetch_in) {
            next_fetch_in = data.next_fetch_in * 1000;
        } else {
            next_fetch_in = 30000;
        }
        Layout.resetNotificationRequestTimeout(next_fetch_in);
    };
    this.viewReport = function (report_id) {
        var w = that.wnd.getOpenFirst(that.wnd.TYPE_REPORT);
        if (w) {
            w.toTop();
            w.getHandler().reportView(report_id);
        } else {
            that.wnd.Create(that.wnd.TYPE_REPORT, 'Reports', {}, report_id, 'view');
        }
    };
    this.viewResTransport = function () {
        var w = that.wnd.getOpenFirst(that.wnd.TYPE_REPORT);
        if (w) {
            w.toTop();
            w.requestContentGet('report', 'resource_transports', {});
        } else {
            that.wnd.Create(that.wnd.TYPE_REPORT, 'Reports', {}, 0, 'resource_transports');
        }
    };
    this.viewMessage = function (message_id) {
        var w = that.wnd.getOpenFirst(that.wnd.TYPE_MESSAGE);
        if (w) {
            w.toTop();
            w.getHandler().messageView(message_id, 'view');
        } else {
            that.wnd.Create(that.wnd.TYPE_MESSAGE, 'Messages', {}, 'view', null, message_id);
        }
    };
    this.viewAttackPlan = function (plan_id) {
        var w = that.wnd.getOpenFirst(that.wnd.TYPE_ATTACK_PLANER);
        if (w) {
            w.toTop();
            w.getHandler().showPlan(plan_id);
        } else {
            that.wnd.Create(that.wnd.TYPE_ATTACK_PLANER, 'Attack planner', {}, 'show_plan', {
                'plan_id': plan_id
            });
        }
    };
    this.notify = function (id, type, title, time, param_id, param_str) {
        var interval = 1E3;
        if (options.disable_notifications) {
            return;
        }
        nStack.push(new Notification({
            'ttl': 1E5,
            'id': id,
            'type': type,
            'html': title ? title : '',
            'time': time,
            'param_id': param_id,
            'param_str': param_str,
            'parent': jQElem.notificationArea
        }));
        jQElem.notificationArea.bind({
            'mouseenter': function (ev) {
                window.clearInterval(timers.notificationQueue);
            },
            'mouseleave': function (ev) {
                if (timers.notificationQueue) {
                    activateQueue();
                }
            }
        });
        switch (type) {
        case 'newmessage':
        case 'awmessage':
            $('#link_messages').addClass('new_message');
            break;
        case 'newreport':
            $('#link_report').addClass('new_report');
            break;
        case 'newalliancepost':
            $('#link_alliance_forum').addClass('new_post');
            break;
        }
    };

    function checkNotitificationQueue() {
        nStack.loop(function (i, elem, arr) {
            if (!elem.age()) {
                arr.remove(i);
            }
        });
        if (!nStack.length) {
            window.clearInterval(timers.notificationQueue);
            delete timers.notificationQueue;
        }
        that.updateTitle();
    }

    function deleteOutdatedNotifications() {
        NotificationStack.deleteOutdated();
    }
    this.deleteAttackPlanerNotifications = function (data_id) {
        var now = Timestamp.now();
        nStack.loop(function (i, elem, arr) {
            if (elem.getType() === NotificationType.PLANED_ATTACK && elem.getTime() < now) {
                nStack[i].destroy();
                arr.remove(i);
            }
        });
    };

    function deleteAllNotifications() {
        that.ajaxPost('notify', 'delete_all', {}, false, function (data) {
            nStack.loop(function (i, elem, arr) {
                elem.destroy();
                arr.remove(i);
            });
        });
    }
    this.handleSystemNotification = function (notification) {
        jQElem.document.trigger('notification.' + notification.subject, notification);
    };
    this.handleCuratorPopupNotification = function (notification) {
        that.ajax('premium_features', 'get_curator_data', {}, false, function (data) {
            if (!$('#overviews_link_hover_menu').length) {
                $(data.overviews_menu).appendTo('#city_overview');
                $('#city_overview_link').removeClass('inactive');
                Layout.bindPremiumStuff();
            }
            if (data.quickbar != '' && $('#toolbar').length <= 0) {
                Quickbar.refresh(data);
            }
            if (BuildingMain.isIndexWindowOpen() && BuildingMain.isBuildFromTownIndexEnabled()) {
                $('#building_tasks_index .main_tasks_hint').remove();
            }
            if (BuildingMain.isMainWindowOpen()) {
                $('#building_tasks_main .main_tasks_hint').remove();
            }
            Layout.showHint('1 free Administrator day!', data.popup, 400, 280, function () {
                Layout.ajaxPost('notify', 'delete', {
                    id: notification.id
                }, false, function (data) {});
                return true;
            });
            $('#free_curator_poup_tutorial_link').bind('click', function () {
                Tutorial.startQuest(Tutorial.TYPE_CURATOR_TUTORIAL);
            });
        });
    };
    this.displayServerTime = function () {
        window.clearInterval(timers.stime);
        timers.stime = window.setInterval(updateServerTime, 1000);
    };
    this.playerID = function (id) {
        if (id) {
            options.playerID = id;
        }
        return options.playerID;
    };
    this.showAjaxLoader = function () {
        if (!elem.ajaxLoader) {
            elem.ajaxLoader = createAjaxLoader();
        }
        elem.ajaxLoader.style.zIndex = 10000;
        elem.ajaxLoader.style.visibility = 'visible';
    };
    this.hideAjaxLoader = function () {
        elem.ajaxLoader.style.visibility = 'hidden';
    };
    this.updateBar = function (bar) {
        var res = this.resources;
        if (!bar) {
            return;
        }
        var town = ITowns.getTown(Game.townId);
        if (bar.storage !== undefined) {
            res.setResources({
                'storage': bar.storage
            });
        }
        if (bar.resources) {
            res.setResourcesContraints(bar.resources_contraints).setResources(bar.resources);
        }
        if (bar.population !== undefined) {
            res.setResources({
                'population': bar.population
            });
        }
        if (bar.favor !== undefined) {
            res.setResources({
                'favor': (bar.favor) ? bar.favor : 0
            });
            PopupFactory.updateProductionPopup('favor', {
                favors: bar.favors
            });
            jQElem.favor.setPopup('favor_production');
            town.allGodsFavors(bar.favors);
            town.powers(bar.powers);
        }
        if (bar.god) {
            jQElem.godMini.hide().attr('class', 'god_mini ' + bar.god).show();
            jQElem.favor.show();
            res.setGod(bar.god);
        } else if (ITowns.getGod(Game.townId) === '') {
            jQElem.godMini[0].className = 'no_god';
        }
        if (bar.casted_powers) {
            that.handleCastedPowersOnOwnTown(bar.casted_powers);
        }
        if (bar.production) {
            res.setProduction({
                wood: bar.production.wood,
                stone: bar.production.stone,
                iron: bar.production.iron,
                favor: bar.current_god_favor_production
            }).updateProductionPopups();
        }
        if (bar.units) {
            town.units(bar.units);
            town.unitsOuter(bar.units_beyond);
            town.unitsSupport(bar.units_support);
            that.unitBar.update();
        }
        if (bar.menu_bubbles) {
            jQElem.document.trigger('menuBubbles', [bar.menu_bubbles]);
        }
        if (bar.player_rank) {
            $('#link_ranking').unbind().click(function () {
                Layout.wnd.Create(Layout.wnd.TYPE_RANKING, 'Ranking', {}, bar.player_rank, Game.townId);
            }).html(bar.player_rank + '.');
        }
        if (bar.report_status) {
            var posts = $('#link_alliance_forum,'),
                reports = $('#link_report'),
                msgs = $('#link_messages');
            posts.toggleClass('new_message', bar.report_status.has_new_alliance_post);
            reports.toggleClass('new_report', bar.report_status.has_new_reports);
            msgs.toggleClass('new_message', bar.report_status.has_new_messages);
        }
        if (bar.building_orders) {
            town.buildingOrdersETC(bar.building_orders);
            that.handleBuildingOrdersETC();
            GPEvents.global.trigger(GPEvents.global.town.building_order_changed);
        }
        if (bar.has_conquerer) {
            town.has_conqueror = true;
            that.conquestWindow.open(Game.townId);
        }
        if (bar.gift && bar.gift.length) {
            var w = that.wnd.getOpenFirst(that.wnd.TYPE_SHOW_ON_LOGIN);
            if (!w) {
                w = that.wnd.Create(that.wnd.TYPE_SHOW_ON_LOGIN);
                w.getHandler().onRcvData(bar.gift);
            }
        }
    };
    this.handleCastedPowersOnOwnTown = function (casted_powers) {
        var i, power_id, clickHandler = function () {
                var power_str = $(this)[0].className.replace("index_town_powers index_town_powers_", "");
                if (power_str != 'pest') {
                    Layout.showPowerExtensionDialog(power_str);
                } else {
                    HumanMessage.error('You cannot extend this spell.');
                }
            },
            timeoutCallback = function () {
                if (casted_powers_on_own_town_timeouts.length <= 1) {
                    jQElem.tLink.removeClass('powers_active');
                }
                $('a.index_town_powers_' + power_id).unbind('click').remove();
                var b = Layout.getBarBitMask();
                Layout.requestBarSubstreamWithNextNotificationTransmission(b.LAYOUT_BAR_CASTED_POWERS | b.LAYOUT_BAR_RESOURCES | b.LAYOUT_BAR_PRODUCTION | b.LAYOUT_BAR_FAVOR);
                Layout.resetNotificationRequestTimeout(500);
            };
        jQElem.tLink.addClass('powers_active');
        jQElem.tLink.parent().find('.town_powers').children().remove();
        i = casted_powers_on_own_town_timeouts.length;
        while (i--) {
            window.clearTimeout(casted_powers_on_own_town_timeouts[i]);
        }
        casted_powers_on_own_town_timeouts = [];
        for (power_id in casted_powers) {
            var finished_at = casted_powers[power_id];
            var div = $('<a href="#" class="index_town_powers index_town_powers_' + power_id + '" style="background-image:url(' + Game.img() + '/game/temple/power_' + power_id + '_16x48.png)"></a>');
            div.bind('click', clickHandler);
            jQElem.tLink.parent().find('.town_powers').append(div);
            var str = PopupFactory.texts[power_id];
            str = '<script type="text/javascript">' + '$("div.temple_power_popup_info span.eta").countdown(' + finished_at + ')' + '</script>' + str.slice(0, str.length - 16) + '<br />' + '<img alt="" src="http://cdn.grepolis.com/images/game/res/time.png">' + '<span class="eta">' + finished_at + '</span>' + '<br />' + (power_id != 'pest' ? this.popupPremiumData(power_id) : '') + '</p></div></div>';
            div.mousePopup(new MousePopup(str));
            casted_powers_on_own_town_timeouts.push(window.setTimeout(timeoutCallback, Math.max(finished_at - Timestamp.now(), 0) * 1E3));
        }
    };
    this.handleBuildingOrdersETC = function () {
        var town = ITowns.getTown(Game.townId);
        var orders_etc = town.buildingOrdersETC();
        var order, i, delay;
        var min_ts = -1;
        for (i in orders_etc) {
            if (orders_etc.hasOwnProperty(i) && ((orders_etc[i].to_be_completed_at < min_ts && orders_etc[i].to_be_completed_at > Timestamp.now()) || min_ts == -1)) {
                min_ts = orders_etc[i].to_be_completed_at;
                order = orders_etc[i];
            }
        }
        if (min_ts == -1) {
            return;
        }
        delay = Math.max((min_ts - Timestamp.now()) * 1000, 10);
        $(document).stopTime('Layout::handleBuildingOrdersETC::timer');
        $(document).oneTime(delay, 'Layout::handleBuildingOrdersETC::timer', function () {
            GPEvents.global.trigger(GPEvents.global.town.building_order_changed, order);
            var bits = Layout.getBarBitMask();
            Layout.requestBarSubstreamWithNextNotificationTransmission(bits.LAYOUT_BAR_BUILDING_ORDERS | bits.LAYOUT_BAR_RESOURCES | bits.LAYOUT_BAR_POPULATION | bits.LAYOUT_BAR_PRODUCTION | bits.LAYOUT_BAR_FAVOR);
            Layout.resetNotificationRequestTimeout(5000);
        });
    };
    this.showPowerExtensionDialog = function (power_id) {
        var title = 'Extend Divine Power';
        var text = 'Are you sure that you want to extend this Divine Power for 20 gold?';
        this.showConfirmDialog(title, text, function () {
            Layout.extendPowerWithGold(power_id);
            this.close();
        });
    };
    this.extendPowerWithGold = function (power_id) {
        Layout.ajax('town_info', 'extend_power', {
            power_id: power_id
        }, false, function (data) {
            BuildOrders.goldChanged(data.available_gold);
            UnitOrder.goldChanged(data.available_gold);
            Kontagent.trackEvent({
                userId: Game.uid,
                eventName: 'ext_pow_' + power_id,
                subtype1: Game.market_id,
                subtype2: Game.world_id,
                subtype3: 'buy_pa_feature'
            }, function () {});
            var ev = GPEvents.global;
            ev.trigger(ev.spend_gold);
        });
    };
    this.popupPremiumData = function (power_id) {
        return '<span class="bold">' + s(ngettext('For %1 gold, you can extend this power by %2 hour the first time you use it. ', new Array('For %1 gold, you can extend this power by %2 hour the first time you use it. ', 'For %1 gold, you can extend this power by %2 hours the first time you use it.'), 20, readableSeconds(GameData.powers[power_id].lifetime * 0.5)), 20, readableSeconds(GameData.powers[power_id].lifetime * 0.5)) + s(ngettext('The second time, by %1 hour.', new Array('The second time, by %1 hour.', 'The second time, by %1 hours.'), readableSeconds(GameData.powers[power_id].lifetime * 0.25)), readableSeconds(GameData.powers[power_id].lifetime * 0.25)) + '</span>';
    };
    this.contextMenu = function (ev, type, town) {
        var radius = 120,
            pos, menu = new ContextMenu({
                'type': type,
                'town': town
            }),
            tileSize;
        var mouseX = ev.clientX + jQElem.document.scrollLeft() - 18;
        var mouseY = ev.clientY + jQElem.document.scrollTop() - 18;

        function openMenu() {
            menu.style.left = mouseX + 'px';
            menu.style.top = mouseY + 'px';
            jQElem.box.parent().append(menu);
            jQElem.document.bind('mousedown.context', function () {
                $(menu).empty().remove();
                menu = null;
                jQElem.document.unbind('mousedown.context');
            });
        }
        var maxX = ($(document).width() - radius),
            maxY = ($(document).height() - radius),
            min = radius,
            l = mouseX < min,
            r = mouseX > maxX,
            t = mouseY < min,
            b = mouseY > maxY,
            xdiff = 0,
            ydiff = 0;
        if (r || l || b || t) {
            pos = WMap.mapPosition;
            if (l) {
                xdiff = -(mouseX - min);
            } else if (r) {
                xdiff = maxX - mouseX;
            }
            if (t) {
                ydiff = -(mouseY - min);
            } else if (b) {
                ydiff = maxY - mouseY;
            }
            mouseX += xdiff;
            mouseY += ydiff;
            WMap.elm.move_container.animate({
                'left': pos.x += xdiff,
                'top': pos.y += ydiff
            }, 300, 'linear', openMenu);
            tileSize = WMap.mapTiles.tileSize;
            WMap.scroll = {
                'x': -pos.x - tileSize.x,
                'y': -pos.y - tileSize.y
            };
        } else {
            openMenu();
        }
    };
    this.getDocument = function () {
        return jQElem.document;
    };
    this.showHint = function (title, text, width, height, onClose, minimizable) {
        if (!width) {
            width = 380;
        }
        if (!height) {
            height = 202;
        }
        return Layout.dialogWindow.open(text, title, width, height, onClose, minimizable);
    };
    this.showConfirmDialog = function (title, text, onConfirmation, confirm_text, cancel_text, onCheck, check_text) {
        var wnd, data = {
            'texts': {
                'confirm': confirm_text || 'Yes',
                'cancel': cancel_text || 'No',
                'check': check_text || null,
                'content': text
            },
            'callback': {
                'confirm': onConfirmation,
                'cancel': null,
                'check': onCheck || null
            }
        };
        wnd = GPWindowMgr.Create(GPWindowMgr.TYPE_CONFIRM_DIALOG, title);
        wnd.getHandler().onRcvData(data);
    };
    this.getButton = function (text, htmlOptions) {
        return '<a href="#" class="button" ' + htmlOptions + ' style="cursor:pointer;display:inline;"><span class="left"><span class="right"><span class="middle">' + text + '</span></span></span><span style="clear:both;"></span></a>';
    };
    this.getUnitOrderImage = function (unit, count) {
        return '<div class="unit unit_order_unit_image" style="background-image: url(http://cdn.grepolis.com/images/game/units/' + unit + '_50x50.png);">' + '<span id="unit_order_count_shadow_' + unit + '" class="bold">' + count + '</span></div>';
    };
    this.closeHint = function () {
        alert('closeHint');
    };
    this.getMenuHeight = function () {
        return null;
    };
    this.getMainArea = function () {
        return jQElem.mainArea;
    };
    this.wnd = GPWindowMgr;
    this.wnd.extendLayoutWithShortLinks(this);
    this.wnd.setMaxConcurrent(options.wndMaxConcurrent);
    this.supportPopup = function (url, width, height) {
        var w = window.open(url, 'popup', 'width=' + width + ',height=' + height + ',resizable=yes,scrollbars=yes');
        w.focus();
    };
    var currentIncommingAttackCount = 0;
    this.setIncommingAttackCount = function (num) {
        var elem = $('#incomming_attack_count');
        if (num == 0 && currentIncommingAttackCount > 0) {
            elem.animate({
                'height': '0px'
            }, 1000, 'linear', function () {
                elem.css('display', 'none');
            });
            NotificationStack.setAdditionalReservedHeight(0);
        } else if (num > 0 && currentIncommingAttackCount == 0) {
            elem.css('display', 'block');
            elem.animate({
                'height': '41px'
            }, 1000, 'linear');
            NotificationStack.setAdditionalReservedHeight(40);
        }
        currentIncommingAttackCount = num;
        $('#incomming_attack_count_text').html(currentIncommingAttackCount);
    };
    if (options.onlyInstantiate === true) {
        return;
    }
    var ajaxloader = new gpAjax(this, false);
    this.ajax = function (controller, action, params, show_loader, callback, method) {
        if (!params) {
            params = {
                town_id: Game.townId
            };
        } else if (!params.town_id) {
            params.town_id = Game.townId;
        }
        params.nlreq_id = Layout.getLastRequestedNotificationId();
        if (method == 'get') {
            ajaxloader.tmpGet(controller, action, params, show_loader, function (_layout, data, flags) {
                callback(data);
            });
        } else if (method == 'post') {
            ajaxloader.tmpPost(controller, action, params, show_loader, function (_layout, data, flags) {
                callback(data);
            });
        } else {
            if (method !== undefined) {
                debug('check & remove old 6th param (used for ajax-call-locking) of ajax call!!!');
            }
            ajaxloader.post(controller, action, params, show_loader, function (_layout, data, flags) {
                callback(data);
            });
        }
    };
    this.ajaxGet = function (controller, action, params, show_loader, callback) {
        this.ajax(controller, action, params, show_loader, callback, 'get');
    };
    this.ajaxPost = function (controller, action, params, show_loader, callback) {
        this.ajax(controller, action, params, show_loader, callback, 'post');
    };
    this.hideAllianceForumLink = function () {
        $('#link_alliance_menu .alliance_forum').hide();
    };
    this.showAllianceForumLink = function () {
        $('#link_alliance_menu .alliance_forum').css({
            display: ''
        });
    };
    window.setInterval(function () {
        var bits = this.getBarBitMask();
        this.requestBarSubstreamWithNextNotificationTransmission(bits.LAYOUT_BAR_NEW_FARM_TOWN_ATTACK_REPORT | bits.LAYOUT_BAR_REPORT_STATUS | bits.LAYOUT_BAR_RANKING);
        this.resetNotificationRequestTimeout(2000);
    }.bind(this), 9E5);
    initialize(options.onInit);
}

function GPLayout(opt) {
    var options = opt,
        that = this,
        jQElem = {
            'notificationArea': $('#notification_area'),
            'delAllNot': $('#delete_all_notifications'),
            'box': $('#box'),
            'document': $(document),
            'mainArea': $('#main_area'),
            'quickbar': $('#toolbar'),
            'godMini': $('#god_mini'),
            'tLink': $('#town_name_link'),
            'favor': $('#favor'),
            'overviewMenu': $('#overviews_link_hover_menu')
        },
        elem = {
            'ajaxLoader': null,
            'serverTime': document.getElementById('server_time')
        },
        nStack = NotificationStack,
        notification_last_requested_id = 0,
        notification_next_fetch_at = 0,
        client_server_time_diff = Timestamp.clientServerDiff(),
        timers = {},
        bar_request_bitmask = 0,
        casted_powers_on_own_town_timeouts = [],
        linkHandling = new LayoutModules.LinkHandling();
    this.townNameList = new LayoutModules.TownNameList();
    opt.resProgressConfig = {
        a: {
            orientation: 'y',
            size: 78,
            segments: 22
        },
        b: {
            orientation: 'y',
            size: 42,
            segments: 25
        }
    };
    this.resources = new LayoutModules.Resources(opt);
    this.unitBar = new LayoutModules.UnitBar();

    function initialize(onInit) {
        bindGlobalEvents();
        bindEvents();
        that.bindResize();
        if (typeof onInit === 'function') {
            that.abPlugin = onInit();
        }
    }

    function createAjaxLoader() {
        var img = document.createElement('img');
        img.id = 'ajax_loader';
        img.src = "http://cdn.grepolis.com/images/game/ajax-loader.gif";
        img.alt = '';
        img.style.top = ((jQElem.box.height() >> 1) - 29) + 'px';
        img.style.left = ((jQElem.box.width() >> 1) - 29) + 'px';
        jQElem.box.append(img);
        return img;
    }

    function bindGlobalEvents() {
        jQElem.document.bind('notification.doRefetchBar', refetchBar);
        $(window).focus(checkCookieTxRxData);
    }

    function checkCookieTxRxData() {
        var data = $.cookie('txrx_lnk'),
            menu;
        if (!data) {
            return;
        }
        $.cookie('txrx_lnk', '{}', {
            expires: -1,
            path: '/'
        });
        data = $.parseJSON(data);
        if (data.com_type && data.com_type == 1) {
            menu = new ContextMenu({
                'type': 'determine',
                'town': data
            });
            $(menu).find('#' + data.cmd).trigger('mousedown').hide().unbind().remove();
        } else if (data.com_type && data.com_type == 2) {
            ReportViewer.dates[data.report_id] = data.report_data;
            if (data.cmd == "all") {
                ReportViewer.insertAllUnitsToSimulator(true, true, data.report_id);
            } else {
                ReportViewer.insertRemainingUnitsToSimulator(true, true, data.report_id);
            }
        }
    }

    function bindEvents() {
        jQElem.document.bind('click.tname', linkHandling.decodeFragment);
        that.bindPremiumStuff();
        jQElem.delAllNot.click(deleteAllNotifications);
        $('#close_all_windows').click(that.wnd.closeAll);
        $('#incomming_attack_count').bind({
            'click': function (ev) {
                if (!$('#city_overview_link').hasClass('inactive')) {
                    Overviews.openOverview('command_overview');
                } else {
                    Layout.wnd.Create(Layout.wnd.TYPE_PREMIUM, 'Premium', {
                        sub_content: 'premium_overview'
                    }, Game.townId);
                }
            },
            'mouseenter': function () {
                $(this).css('cursor', 'pointer');
            },
            'mouseleave': function () {
                $(this).css('cursor', 'auto');
            }
        });
        $('#incomming_attack_count').mousePopup(new MousePopup('Arriving attacks'));
        $('#toggle_powers').click(switchSidebar);
        var popup_div = $('#popup_div');
        popup_div.unbind('.popup').bind({
            'mouseleave.popup': function () {
                $('#popup_div').hide().clearQueue();
            }
        });
    }

    function handleLinks(e) {
        var type;
        if (!e.target.href) {
            return;
        }
        type = e.target.href.split(/#/)[1];
        LayoutModules.LinkHandling.Menu.followLink(that, type);
        if (type != undefined) {
            e.preventDefault();
        }
    }

    function switchSidebar() {
        if (that.unitBar instanceof LayoutModules.UnitBar) {
            that.unitBar.destroy();
            that.unitBar = new LayoutModules.PowerBar();
        } else if (that.unitBar instanceof LayoutModules.PowerBar) {
            that.unitBar.destroy();
            that.unitBar = new LayoutModules.UnitBar();
        }
        that.unitBar.update();
    }
    this.bindPremiumStuff = function (type) {
        if (type) {
            jQElem.overviewMenu = [];
        }
        if (!jQElem.overviewMenu.length) {
            jQElem.overviewMenu = $('#overviews_link_hover_menu');
        }
        $('#city_overview_link').unbind().bind({
            'click': function () {
                switch (type || this.className) {
                case 'inactive':
                    Layout.wnd.Create(Layout.wnd.TYPE_PREMIUM, 'Premium', {
                        sub_content: 'premium_overview'
                    }, Game.townId);
                    break;
                case 'curator':
                    Overviews.openOverview('trade_overview');
                    break;
                case 'captain':
                    Layout.wnd.Create(Layout.wnd.TYPE_FARM_TOWN_OVERVIEWS, 'Farming villages', {});
                    break;
                }
            },
            'mouseenter': function (ev) {
                if (!jQElem.overviewMenu) {
                    return;
                }
                jQElem.overviewMenu.appendTo('body').show();
            },
            'mouseleave': function (ev) {
                if (!jQElem.overviewMenu.length) {
                    return;
                }
                var menu = jQElem.overviewMenu;
                var elm_off_x = menu.offset().left;
                if (ev.clientX < elm_off_x || ev.clientX > elm_off_x + 21 || ev.clientY < 28) {
                    menu.hide();
                }
                if (menu.hover()) {
                    menu.hover(function () {}, function () {
                        menu.hide();
                    });
                }
            }
        });
        jQElem.overviewMenu.click(function (e) {
            var name = e.target.name;
            switch (name) {
            case 'town_group_overview':
                Overviews.openOverview(name, name + 's');
                break;
            case 'farm_town_overview':
                Layout.wnd.Create(Layout.wnd.TYPE_FARM_TOWN_OVERVIEWS, 'Farming villages', {});
                break;
            default:
                Overviews.openOverview(name);
            }
        });
        this.bindMenuBubbleClickHandler('troops');
        this.bindMenuBubbleClickHandler('movement');
        this.bindMenuBubbleClickHandler('trade');
    };

    function updateServerTime() {
        var servertime, current_server_time = (new Date()).getTime() - client_server_time_diff * 1E3;
        servertime = new Date(current_server_time + Timestamp.serverGMTOffset() * 1E3);
        elem.serverTime.innerHTML = getHumanReadableTimeDate(servertime);
        if ($('#server_time_popup').length) {
            servertime = new Date(current_server_time + Timestamp.localeGMTOffset() * 1E3);
            $('#server_time_popup').html(getHumanReadableTimeDate(servertime));
        }
    }
    this.showMemoWindow = function () {
        if (!$('#city_overview_link').hasClass('inactive')) {
            Layout.ajaxGet('player_memo', 'load_memo_content', {}, false, function (data) {
                var wnd_memo = Layout.wnd.Create(Layout.wnd.TYPE_MEMO, '', {
                    data: data
                });
                if (wnd_memo) {
                    wnd_memo.sendMessage('setData', data);
                }
            });
        } else {
            Layout.wnd.Create(Layout.wnd.TYPE_PREMIUM, 'Premium', {
                sub_content: 'premium_overview'
            }, Game.townId);
        }
    };
    GPEvents.global.bind(GPEvents.global.unit_orders.unit_finished, handleUnitOrderFinished);

    function handleUnitOrderFinished(event, data) {
        var town = ITowns.getTown(Game.townId);
        var units = {};
        units[data.unit_id] = 1;
        town.addUnits(units);
        that.unitBar.update();
    }
    GPEvents.global.bind(GPEvents.global.system_notifications.conquest_info, handleConquerAndColonizationUpdate);
    GPEvents.global.bind(GPEvents.global.system_notifications.uninhabited_place_colonized, handleConquerAndColonizationUpdate);

    function handleConquerAndColonizationUpdate(event, notification) {
        var i, towns, n;
        if (jQElem.tListDropdown.is(':visible')) {
            jQElem.tListDropdown.hide().empty();
        }
        switch (event.type) {
        case GPEvents.global.system_notifications.conquest_info:
            if (notification.param_id == 0) {
                towns = ITowns.getTowns();
                n = 0;
                for (i in towns) {
                    if (!towns.hasOwnProperty(i)) {
                        continue;
                    }
                    n++;
                }
                if (n <= 1) {
                    Layout.ajaxPost('player', 'logout', {}, true, function (data) {});
                    return;
                } else if (notification.param_str.town_id == Game.townId) {
                    Layout.nextTown();
                }
            }
            break;
        case GPEvents.global.system_notifications.uninhabited_place_colonized:
            break;
        default:
            break;
        }
        WMap.pollForMapChunksUpdate();
        ITowns.refetch(function () {
            Layout.townNameList.createTownListDropdown();
            $('#town_list').hide();
        });
        var w = Layout.wnd.getOpenFirst(Layout.wnd.TYPE_TOWN_OVERVIEWS);
        if (w) {
            w.getHandler().sendMessage('onReload');
        }
    }
    this.updateTitle = function () {
        var len = nStack.length();
        document.title = 'Grepolis' + (len ? ' (' + len + ')' : '') + ' - ' + Game.townName;
    };
    this.bindResize = function (refetchElem) {
        var is_top = true,
            minwidth = 1523;
        if (!jQElem.quickbar.length || refetchElem === true) {
            jQElem.quickbar = $('#toolbar');
        }
        if (!jQElem.quickbar.length) {
            jQElem.quickbar = $('#emailbar');
        }
        if (!jQElem.quickbar.length) {
            jQElem.quickbar = $('#chargebackbar');
        }
        arrangeQuickbar();
        $(window).unbind("resize", arrangeQuickbar).resize(arrangeQuickbar);

        function arrangeQuickbar() {
            if (is_top && this.document.body.clientWidth < minwidth) {
                jQElem.quickbar.css({
                    top: '65px'
                });
                return (is_top = false);
            } else if (!is_top && this.document.body.clientWidth >= minwidth) {
                jQElem.quickbar.css({
                    top: '30px'
                });
                return (is_top = true);
            }
        }
    };
    this.townSwitch = function (townID) {
        if (townID == Game.townId) {
            return;
        }
        var new_town = ITowns.getTown(townID);
        Game.townId = townID;
        Game.townName = new_town.name;
        $('#town_name_input').val(Game.townName);
        jQElem.tLink.text(Game.townName);
        that.updateTitle();
        $('#town_name_link').removeClass('powers_active').parent().find('div.town_powers').children().remove();
        var building_wnd = that.buildingWindow.getWnd();
        if (building_wnd) {
            that.buildingWindow.refresh();
        }
        var townindex_wnd = that.townIndex.getWnd();
        if (townindex_wnd) {
            that.townIndex.open(Game.townId, Game.townName);
        }
        WMap.setCurrentTown(Game.townId, new_town.island_x, new_town.island_y);
        that.ajaxGet('index', 'switch_town', {}, true, function (data) {
            if (!hasValidTownToken(data)) {
                return;
            }
            if (data.bar) {
                that.updateBar(data.bar);
            }
            jQElem.document.trigger(GPEvents.global.town.town_switch);
            if (new_town.has_conqueror) {
                that.conquestWindow.open(Game.townId);
            } else {
                that.conquestWindow.close();
            }
        });
    };
    this.bindMenuBubbleClickHandler = function (name, obj) {
        var has_premium = !$('#city_overview_link').hasClass('inactive');
        if (!obj) {
            obj = $('#icon_' + name);
        }
        obj.unbind('click');
        if (has_premium) {
            var overview_actions = {
                'troops': 'recruit_overview',
                'movement': 'command_overview',
                'trade': 'trade_overview'
            };
            obj.click(function () {
                Overviews.openOverview(overview_actions[name]);
            });
        } else {
            obj.click(function () {
                Layout.wnd.Create(Layout.wnd.TYPE_PREMIUM, 'Premium', {
                    sub_content: 'premium_overview'
                }, Game.townId);
            });
        }
    };
    var g_MaintenanceBlockActive = false;
    this.isMaintenanceBlocked = function () {
        return g_MaintenanceBlockActive;
    };
    this.blockForMaintenance = function (url) {
        if (g_MaintenanceBlockActive) {
            return;
        }
        g_MaintenanceBlockActive = true;
        jQuery.blocker({
            'html': $("<div><div style=\"text-align: center;\"><b>" + 'Maintenance mode' + "</b></div>" + "<div style=\"text-align: center; margin-top: 15px;\">" + 'Sorry, we\'re currently doing maintenance. You were automatically logged out!' + "</div><br><br></div>").append(button('Back to the login', {
                style: "float: right;",
                onClick: "jQuery.blocker.unblock();",
                href: "#"
            })),
            'caching': false,
            'callback': function () {
                window.location = url;
            }
        });
    };
    var tutorial_reshighlight_arrow = null;
    this.tutorialResourceHighlight = function (on_population) {
        if (on_population === true) {
            tutorial_reshighlight_arrow = Tutorial.makeArrow('#res', 'e', {
                x: 250,
                y: 4
            });
            tutorial_reshighlight_arrow.bounce();
        } else {
            tutorial_reshighlight_arrow = Tutorial.makeArrow('#res', 'w', {
                x: -170,
                y: 4
            });
            tutorial_reshighlight_arrow.bounce();
        }
    };
    this.stopTutorialResourceHighlight = function () {
        if (tutorial_reshighlight_arrow != null) {
            tutorial_reshighlight_arrow.remove();
            tutorial_reshighlight_arrow = null;
        }
    };
    var tutorial_cityindex_arrows = [];
    this.tutorialShowCityIndexArrows = function () {
        tutorial_cityindex_arrows[0] = Tutorial.makeArrow('#city_index', 'n', {
            x: 0,
            y: 30
        });
        tutorial_cityindex_arrows[0].bounce();
    };
    this.stopTutorialCityIndexArrows = function () {
        if (tutorial_cityindex_arrows[0] != null) {
            tutorial_cityindex_arrows[0].remove();
            tutorial_cityindex_arrows[0] = null;
        }
    };
    var tutorial_menububblehighlight_arrow = null;
    this.tutorialMenuBubbleHighlight = function () {
        tutorial_menububblehighlight_arrow = Tutorial.makeArrow('#icon_movement', 'w', {
            x: -5,
            y: 30
        });
        tutorial_menububblehighlight_arrow.bounce();
    };
    this.stopTutorialMenuBubbleHighlight = function () {
        if (tutorial_menububblehighlight_arrow != null) {
            tutorial_menububblehighlight_arrow.remove();
            tutorial_menububblehighlight_arrow = null;
        }
    };
    this.getBarBitMask = function () {
        return {
            LAYOUT_BAR_RESOURCES: 2,
            LAYOUT_BAR_FAVOR: 4,
            LAYOUT_BAR_GOD: 8,
            LAYOUT_BAR_POPULATION: 16,
            LAYOUT_BAR_REPORT_STATUS: 32,
            LAYOUT_BAR_NEW_FARM_TOWN_ATTACK_REPORT: 64,
            LAYOUT_BAR_HAS_CONQUERER: 128,
            LAYOUT_BAR_BUILDING_ORDERS: 256,
            LAYOUT_BAR_CASTED_POWERS: 512,
            LAYOUT_BAR_UNITS: 1024,
            LAYOUT_BAR_FAVOR_GODS_OVERVIEW: 2048,
            LAYOUT_BAR_MENU_BUBBLES: 4096,
            LAYOUT_BAR_PRODUCTION: 8192,
            LAYOUT_BAR_RANKING: 16384,
            LAYOUT_BAR_GIFT: 65536,
            LAYOUT_BAR_FULL: 131071
        };
    };

    function refetchBar(event, notification) {
        if (notification.param_id == Game.townId) {
            Layout.requestBarSubstreamWithNextNotificationTransmission(notification.param_str);
            Layout.resetNotificationRequestTimeout(3000);
            that.resources.updateProductionPopups();
        }
    }
    this.refetchBar = function (event, notification) {
        refetchBar(event, notification);
    };
    this.requestBarSubstreamWithNextNotificationTransmission = function (bar_bitmask) {
        bar_request_bitmask = bar_bitmask >= 0 ? bar_bitmask : 0;
    };
    this.getLastRequestedNotificationId = function () {
        return notification_last_requested_id;
    };
    this.requestNotifications = function (_no_sysmsg) {
        if (!_no_sysmsg) {
            _no_sysmsg = false;
        }
        notification_next_fetch_at = 0;
        this.ajaxGet('notify', 'fetch', {
            bar_mask: bar_request_bitmask,
            'no_sysmsg': _no_sysmsg
        }, false, function () {});
        bar_request_bitmask = 0;
    };
    this.resetNotificationRequestTimeout = function (next_fetch_in) {
        next_fetch_in = next_fetch_in || 10;
        var now = Timestamp.now('c');
        if (notification_next_fetch_at < now || (notification_next_fetch_at > now && notification_next_fetch_at > now + Math.ceil(next_fetch_in * 0.001))) {
            notification_next_fetch_at = now + Math.ceil(next_fetch_in * 0.001);
            if (timers.notificationrequesttimeout) {
                window.clearTimeout(timers.notificationrequesttimeout);
            }
            timers.notificationrequesttimeout = window.setTimeout(function () {
                Layout.requestNotifications();
            }, next_fetch_in);
        }
    };
    this.recvNotifyData = function (data) {
        var notification, i = data.notifications.length,
            id, showOnLogin;
        deleteOutdatedNotifications();
        if (data.incomming_attack_count !== undefined) {
            that.setIncommingAttackCount(~~data.incomming_attack_count);
        }
        data.notifications.reverse();
        while (i--) {
            notification = data.notifications[i];
            id = parseInt(notification.id, 10);
            if (notification_last_requested_id < id) {
                Game.notification_last_requested_id = notification_last_requested_id = id;
            }
            switch (notification.type) {
            case NotificationType.SHOW_CURATOR_POPUP:
                that.handleCuratorPopupNotification(notification);
                break;
            case NotificationType.SYSTEMMESSAGE:
                that.handleSystemNotification(notification);
                break;
            case NotificationType.QUEST:
                Tutorial.handleNotification(notification);
                break;
            case NotificationType.INCOMING_ATTACK:
                if (that.player_hint_settings.militia_hint && !that.militia_hint_shown) {
                    showOnLogin = {
                        title: 'Warning, you\'re being attacked!',
                        text: '<p>' + '<span class="index_unit" style="float:left;"><img src="http://cdn.grepolis.com/images/game/units/militia_40x40.png" alt="militia" /></span>' + 'Arm your city residents and call in the militia to defend yourself better. You can find further information at the farm.' + '</p><hr /><a class="confirm" href="#confirm"></a>',
                        type: 'hint.militia'
                    };
                    (GPWindowMgr.Create(GPWindowMgr.TYPE_SHOW_ON_LOGIN) || GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_SHOW_ON_LOGIN)).getHandler().onRcvData(showOnLogin);
                    that.militia_hint_shown = true;
                }
                that.notify(notification.id, notification.type, notification.subject + '<br/><span class="small">' + 'Arrival:' + readableUnixTimestamp(notification.time, 'player_timezone', {
                    extended_date: true
                }) + '</span>', notification.time, notification.param_id, notification.param_str);
                break;
            case NotificationType.INCOMING_SUPPORT:
                that.notify(notification.id, notification.type, notification.subject + '<br/><span class="small">' + 'Arrival:' + readableUnixTimestamp(notification.time, 'player_timezone', {
                    extended_date: true
                }) + '</span>', notification.time, notification.param_id, notification.param_str);
                break;
            default:
                if (notification.type == NotificationType.PHOENICIAN_SALESMAN_ARRIVED || notification.type == NotificationType.PHOENICIAN_SALESMAN_LEAVE) {
                    var town = ITowns.getTown(notification.param_id);
                    if (town == undefined) {
                        Layout.ajaxPost('notify', 'delete', {
                            id: notification.id
                        }, false);
                        continue;
                    }
                }
                notification.subject = '<a class="notify_subjectlink" href="#">' + notification.subject + '</a>';
                that.notify(notification.id, notification.type, notification.subject + '<span class="small notification_date">' + readableUnixTimestamp(notification.time, 'player_timezone', {
                    extended_date: true
                }) + '</span>', notification.time, notification.param_id, notification.param_str);
                break;
            }
        }
        var next_fetch_in;
        if (data.next_fetch_in) {
            next_fetch_in = data.next_fetch_in * 1000;
        } else {
            next_fetch_in = 30000;
        }
        Layout.resetNotificationRequestTimeout(next_fetch_in);
    };
    this.viewReport = function (report_id) {
        var w = that.wnd.getOpenFirst(that.wnd.TYPE_REPORT);
        if (w) {
            w.toTop();
            w.getHandler().reportView(report_id);
        } else {
            that.wnd.Create(that.wnd.TYPE_REPORT, 'Reports', {}, report_id, 'view');
        }
    };
    this.viewResTransport = function () {
        var w = that.wnd.getOpenFirst(that.wnd.TYPE_REPORT);
        if (w) {
            w.toTop();
            w.requestContentGet('report', 'resource_transports', {});
        } else {
            that.wnd.Create(that.wnd.TYPE_REPORT, 'Reports', {}, 0, 'resource_transports');
        }
    };
    this.viewMessage = function (message_id) {
        var w = that.wnd.getOpenFirst(that.wnd.TYPE_MESSAGE);
        if (w) {
            w.toTop();
            w.getHandler().messageView(message_id, 'view');
        } else {
            that.wnd.Create(that.wnd.TYPE_MESSAGE, 'Messages', {}, 'view', null, message_id);
        }
    };
    this.viewAttackPlan = function (plan_id) {
        var w = that.wnd.getOpenFirst(that.wnd.TYPE_ATTACK_PLANER);
        if (w) {
            w.toTop();
            w.getHandler().showPlan(plan_id);
        } else {
            that.wnd.Create(that.wnd.TYPE_ATTACK_PLANER, 'Attack planner', {}, 'show_plan', {
                'plan_id': plan_id
            });
        }
    };
    this.notify = function (id, type, title, time, param_id, param_str) {
        var interval = 1E3;
        if (options.disable_notifications) {
            return;
        }
        nStack.push(new Notification({
            'ttl': 1E5,
            'id': id,
            'type': type,
            'html': title ? title : '',
            'time': time,
            'param_id': param_id,
            'param_str': param_str,
            'parent': jQElem.notificationArea
        }));
        jQElem.notificationArea.bind({
            'mouseenter': function (ev) {
                window.clearInterval(timers.notificationQueue);
            },
            'mouseleave': function (ev) {
                if (timers.notificationQueue) {
                    activateQueue();
                }
            }
        });
        switch (type) {
        case 'newmessage':
        case 'awmessage':
            $('#new_messages').show();
            break;
        case 'newreport':
            $('#new_reports').show();
            break;
        case 'newalliancepost':
            $('#new_post').toggleClass('new_post no_new_post');
            break;
        }
    };

    function checkNotitificationQueue() {
        nStack.loop(function (i, elem, arr) {
            if (!elem.age()) {
                arr.remove(i);
            }
        });
        if (!nStack.length) {
            window.clearInterval(timers.notificationQueue);
            delete timers.notificationQueue;
        }
        that.updateTitle();
    }

    function deleteOutdatedNotifications() {
        NotificationStack.deleteOutdated();
    }
    this.deleteAttackPlanerNotifications = function (data_id) {
        var now = Timestamp.now();
        nStack.loop(function (i, elem, arr) {
            if (elem.getType() === NotificationType.PLANED_ATTACK && elem.getTime() < now) {
                nStack[i].destroy();
                arr.remove(i);
            }
        });
    };

    function deleteAllNotifications() {
        if (!Tutorial || !Tutorial.isActive()) {
            that.ajaxPost('notify', 'delete_all', {}, false, function (data) {
                nStack.loop(function (i, elem, arr) {
                    elem.destroy();
                    arr.remove(i);
                });
            });
        } else {
            HumanMessage.error('Sorry, this action isn\'t possible, please finish the tutorial first');
        }
    }
    this.handleSystemNotification = function (notification) {
        jQElem.document.trigger('notification.' + notification.subject, notification);
    };
    this.handleCuratorPopupNotification = function (notification) {
        var showOnLogin;
        that.ajaxGet('premium_features', 'get_curator_data', {}, false, function (data) {
            if (!$('#overviews_link_hover_menu').length) {
                $(data.overviews_menu).appendTo('#city_overview');
                $('#city_overview_link').removeClass('inactive');
                Layout.bindPremiumStuff();
            }
            if (data.quickbar != '' && $('#toolbar').length <= 0) {
                Quickbar.refresh(data);
            }
            if (BuildingMain.isIndexWindowOpen() && BuildingMain.isBuildFromTownIndexEnabled()) {
                $('#building_tasks_index .main_tasks_hint').remove();
            }
            if (BuildingMain.isMainWindowOpen()) {
                $('#building_tasks_main .main_tasks_hint').remove();
            }
            showOnLogin = {
                title: '1 free Administrator day!',
                text: data.popup,
                type: 'hint.curator',
                confirm: function () {
                    Layout.ajaxPost('notify', 'delete', {
                        id: notification.id
                    }, false, function (data) {});
                    return true;
                }
            };
            (GPWindowMgr.Create(GPWindowMgr.TYPE_SHOW_ON_LOGIN) || GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_SHOW_ON_LOGIN)).getHandler().onRcvData(showOnLogin);
            $('#free_curator_poup_tutorial_link').bind('click', function () {
                Tutorial.startQuest(Tutorial.TYPE_CURATOR_TUTORIAL);
            });
        });
    };
    this.displayServerTime = function () {
        window.clearInterval(timers.stime);
        timers.stime = window.setInterval(updateServerTime, 1000);
    };
    this.playerID = function (id) {
        if (id) {
            options.playerID = id;
        }
        return options.playerID;
    };
    this.showAjaxLoader = function () {
        if (!elem.ajaxLoader) {
            elem.ajaxLoader = createAjaxLoader();
        }
        elem.ajaxLoader.style.zIndex = 10000;
        elem.ajaxLoader.style.visibility = 'visible';
    };
    this.hideAjaxLoader = function () {
        elem.ajaxLoader.style.visibility = 'hidden';
    };
    this.updateBar = function (bar) {
        var res = this.resources;
        if (!bar) {
            return;
        }
        var town = ITowns.getTown(Game.townId);
        if (bar.storage !== undefined) {
            res.setResources({
                'storage': bar.storage
            });
        }
        if (bar.resources) {
            res.setResourcesContraints(bar.resources_contraints).setResources(bar.resources);
        }
        if (bar.population !== undefined) {
            res.setResources({
                'population': bar.population
            });
        }
        if (bar.favor !== undefined) {
            res.setResources({
                'favor': (bar.favor) ? bar.favor : 0
            });
            PopupFactory.updateProductionPopup('favor', {
                favors: bar.favors
            });
            jQElem.favor.setPopup('favor_production');
            town.allGodsFavors(bar.favors);
            town.powers(bar.powers);
        }
        if (bar.god) {
            jQElem.godMini.hide().attr('class', 'god_mini ' + bar.god).show();
            jQElem.favor.show();
            res.setGod(bar.god);
        } else if (ITowns.getGod(Game.townId) === '') {
            jQElem.godMini[0].className = 'no_god';
        }
        if (bar.casted_powers) {
            that.handleCastedPowersOnOwnTown(bar.casted_powers);
        }
        if (bar.production) {
            res.setProduction({
                wood: bar.production.wood,
                stone: bar.production.stone,
                iron: bar.production.iron,
                favor: bar.current_god_favor_production
            }).updateProductionPopups();
        }
        if (bar.units) {
            town.units(bar.units);
            town.unitsOuter(bar.units_beyond);
            town.unitsSupport(bar.units_support);
            that.unitBar.update();
        }
        if (bar.menu_bubbles) {
            jQElem.document.trigger('menuBubbles', [bar.menu_bubbles]);
        }
        if (bar.player_rank) {
            $('#link_ranking a').unbind().click(function () {
                Layout.wnd.Create(Layout.wnd.TYPE_RANKING, 'Ranking', {}, bar.player_rank, Game.townId);
            }).html('Ranking' + ' (' + bar.player_rank + '.)');
        }
        if (bar.report_status) {
            var new_post = document.getElementById('new_post'),
                new_reports = document.getElementById('new_reports'),
                new_messages = document.getElementById('new_messages');
            if (new_post.className == 'new_post' && !bar.report_status.has_new_alliance_post) {
                new_post.className = 'no_new_post';
            }
            if (new_post.className == 'no_new_post' && bar.report_status.has_new_alliance_post) {
                new_post.className = 'new_post';
            }
            if (new_reports.className == 'message_icon' && !bar.report_status.has_new_reports) {
                new_reports.className = 'no_new_messages';
            }
            if (new_reports.className == 'no_new_messages' && bar.report_status.has_new_reports) {
                new_reports.className = 'message_icon';
            }
            if (new_messages.className == 'message_icon' && !bar.report_status.has_new_messages) {
                new_messages.className = 'no_new_messages';
            }
            if (new_messages.className == 'no_new_messages' && bar.report_status.has_new_messages) {
                new_messages.className = 'message_icon';
            }
        }
        if (bar.building_orders) {
            town.buildingOrdersETC(bar.building_orders);
            that.handleBuildingOrdersETC();
            GPEvents.global.trigger(GPEvents.global.town.building_order_changed);
        }
        if (bar.has_conquerer) {
            town.has_conqueror = true;
            that.conquestWindow.open(Game.townId);
        }
        if (bar.gift && bar.gift.length) {
            var w = that.wnd.getOpenFirst(that.wnd.TYPE_SHOW_ON_LOGIN);
            if (!w) {
                w = that.wnd.Create(that.wnd.TYPE_SHOW_ON_LOGIN);
                w.getHandler().onRcvData(bar.gift);
            }
        }
    };
    this.handleCastedPowersOnOwnTown = function (casted_powers) {
        var i, power_id, clickHandler = function () {
                var power_str = $(this)[0].className.replace("index_town_powers index_town_powers_", "");
                if (power_str != 'pest' && power_str != 'pumpkin') {
                    Layout.showPowerExtensionDialog(power_str);
                } else {
                    HumanMessage.error('You cannot extend this spell.');
                }
            },
            timeoutCallback = function () {
                if (casted_powers_on_own_town_timeouts.length <= 1) {
                    jQElem.tLink.removeClass('powers_active');
                }
                $('a.index_town_powers_' + power_id).unbind('click').remove();
                var b = Layout.getBarBitMask();
                Layout.requestBarSubstreamWithNextNotificationTransmission(b.LAYOUT_BAR_CASTED_POWERS | b.LAYOUT_BAR_RESOURCES | b.LAYOUT_BAR_PRODUCTION | b.LAYOUT_BAR_FAVOR);
                Layout.resetNotificationRequestTimeout(500);
            };
        jQElem.tLink.addClass('powers_active');
        jQElem.tLink.parent().find('.town_powers').children().remove();
        i = casted_powers_on_own_town_timeouts.length;
        while (i--) {
            window.clearTimeout(casted_powers_on_own_town_timeouts[i]);
        }
        casted_powers_on_own_town_timeouts = [];
        for (power_id in casted_powers) {
            var finished_at = casted_powers[power_id];
            var div = $('<a href="#" class="index_town_powers index_town_powers_' + power_id + '" style="background-image:url(' + Game.img() + '/game/temple/power_' + power_id + '_16x48.png)"></a>');
            div.bind('click', clickHandler);
            jQElem.tLink.parent().find('.town_powers').append(div);
            var str = PopupFactory.texts[power_id];
            str = '<script type="text/javascript">' + '$("div.temple_power_popup_info span.eta").countdown(' + finished_at + ')' + '</script>' + str.slice(0, str.length - 16) + '<br />' + '<img alt="" src="http://cdn.grepolis.com/images/game/res/time.png">' + '<span class="eta">' + finished_at + '</span>' + '<br />' + (power_id != 'pest' && power_id != 'pumpkin' ? this.popupPremiumData(power_id) : '') + '</p></div></div>';
            div.mousePopup(new MousePopup(str));
            casted_powers_on_own_town_timeouts.push(window.setTimeout(timeoutCallback, Math.max(finished_at - Timestamp.now(), 0) * 1E3));
        }
    };
    this.handleBuildingOrdersETC = function () {
        var town = ITowns.getTown(Game.townId);
        var orders_etc = town.buildingOrdersETC();
        var order, i, delay;
        var min_ts = -1;
        for (i in orders_etc) {
            if (orders_etc.hasOwnProperty(i) && ((orders_etc[i].to_be_completed_at < min_ts && orders_etc[i].to_be_completed_at > Timestamp.now()) || min_ts == -1)) {
                min_ts = orders_etc[i].to_be_completed_at;
                order = orders_etc[i];
            }
        }
        if (min_ts == -1) {
            return;
        }
        delay = Math.max((min_ts - Timestamp.now()) * 1000, 10);
        $(document).stopTime('Layout::handleBuildingOrdersETC::timer');
        $(document).oneTime(delay, 'Layout::handleBuildingOrdersETC::timer', function () {
            GPEvents.global.trigger(GPEvents.global.town.building_order_changed, order);
            var bits = Layout.getBarBitMask();
            Layout.requestBarSubstreamWithNextNotificationTransmission(bits.LAYOUT_BAR_BUILDING_ORDERS | bits.LAYOUT_BAR_RESOURCES | bits.LAYOUT_BAR_POPULATION | bits.LAYOUT_BAR_PRODUCTION | bits.LAYOUT_BAR_FAVOR);
            Layout.resetNotificationRequestTimeout(5000);
        });
    };
    this.showPowerExtensionDialog = function (power_id) {
        if (this.show_confirmation_popup) {
            var title = 'Extend Divine Power';
            var text = 'Are you sure that you want to extend this Divine Power for 20 gold?';
            this.showConfirmDialog(title, text, function () {
                Layout.extendPowerWithGold(power_id);
            }, null, null, null, null, 'Do not show this window again');
        } else {
            Layout.extendPowerWithGold(power_id);
        }
    };
    this.extendPowerWithGold = function (power_id) {
        Layout.ajax('town_info', 'extend_power', {
            power_id: power_id
        }, false, function (data) {
            BuildOrders.goldChanged(data.available_gold);
            UnitOrder.goldChanged(data.available_gold);
            Kontagent.trackEvent({
                userId: Game.uid,
                eventName: 'ext_pow_' + power_id,
                subtype1: Game.market_id,
                subtype2: Game.world_id,
                subtype3: 'buy_pa_feature'
            }, function () {});
            var ev = GPEvents.global;
            ev.trigger(ev.spend_gold);
        });
    };
    this.popupPremiumData = function (power_id) {
        return '<span class="bold">' + s(ngettext('For %1 gold, you can extend this power by %2 hour the first time you use it. ', new Array('For %1 gold, you can extend this power by %2 hour the first time you use it. ', 'For %1 gold, you can extend this power by %2 hours the first time you use it.'), 20, readableSeconds(GameData.powers[power_id].lifetime * 0.5)), 20, readableSeconds(GameData.powers[power_id].lifetime * 0.5)) + s(ngettext('The second time, by %1 hour.', new Array('The second time, by %1 hour.', 'The second time, by %1 hours.'), readableSeconds(GameData.powers[power_id].lifetime * 0.25)), readableSeconds(GameData.powers[power_id].lifetime * 0.25)) + '</span>';
    };
    this.contextMenu = function (ev, type, town) {
        var radius = 120,
            pos, menu = new ContextMenu({
                'type': type,
                'town': town
            }),
            tileSize;
        var mouseX = ev.clientX + jQElem.document.scrollLeft() - 18;
        var mouseY = ev.clientY + jQElem.document.scrollTop() - 18;

        function openMenu() {
            menu.style.left = mouseX + 'px';
            menu.style.top = mouseY + 'px';
            jQElem.box.parent().append(menu);
            jQElem.document.bind('mousedown.context', function () {
                $(menu).empty().remove();
                menu = null;
                jQElem.document.unbind('mousedown.context');
            });
        }
        var maxX = ($(document).width() - radius),
            maxY = ($(document).height() - radius),
            min = radius,
            l = mouseX < min,
            r = mouseX > maxX,
            t = mouseY < min,
            b = mouseY > maxY,
            xdiff = 0,
            ydiff = 0;
        if (r || l || b || t) {
            pos = WMap.mapPosition;
            if (l) {
                xdiff = -(mouseX - min);
            } else if (r) {
                xdiff = maxX - mouseX;
            }
            if (t) {
                ydiff = -(mouseY - min);
            } else if (b) {
                ydiff = maxY - mouseY;
            }
            mouseX += xdiff;
            mouseY += ydiff;
            WMap.elm.move_container.animate({
                'left': pos.x += xdiff,
                'top': pos.y += ydiff
            }, 300, 'linear', openMenu);
            tileSize = WMap.mapTiles.tileSize;
            WMap.scroll = {
                'x': -pos.x - tileSize.x,
                'y': -pos.y - tileSize.y
            };
        } else {
            openMenu();
        }
    };
    this.getDocument = function () {
        return jQElem.document;
    };
    this.showHint = function (title, text, width, height, onClose, minimizable) {
        if (!width) {
            width = 380;
        }
        if (!height) {
            height = 202;
        }
        return this.dialogWindow.open(text, title, width, height, onClose, minimizable);
    };
    this.showConfirmDialog = function (title, text, onConfirmation, confirm_text, onCancel, cancel_text, onCheck, check_text) {
        var wnd, data = {
            'texts': {
                'confirm': confirm_text || 'Yes',
                'cancel': cancel_text || 'No',
                'check': check_text || null,
                'content': text
            },
            'callback': {
                'confirm': onConfirmation,
                'cancel': onCancel || null,
                'check': onCheck || null
            }
        };
        wnd = GPWindowMgr.Create(GPWindowMgr.TYPE_CONFIRM_DIALOG, title);
        wnd.getHandler().onRcvData(data);
    };
    this.getButton = function (text, htmlOptions) {
        return '<a href="#" class="button" ' + htmlOptions + ' style="cursor:pointer;display:inline;"><span class="left"><span class="right"><span class="middle">' + text + '</span></span></span><span style="clear:both;"></span></a>';
    };
    this.getUnitOrderImage = function (unit, count) {
        return '<div class="unit unit_order_unit_image" style="background-image: url(http://cdn.grepolis.com/images/game/units/' + unit + '_50x50.png);">' + '<span id="unit_order_count_shadow_' + unit + '" class="bold">' + count + '</span></div>';
    };
    this.closeHint = function () {
        alert('closeHint');
    };
    this.getMenuHeight = function () {
        return null;
    };
    this.getMainArea = function () {
        return jQElem.mainArea;
    };
    this.wnd = GPWindowMgr;
    this.wnd.extendLayoutWithShortLinks(this);
    this.wnd.setMaxConcurrent(options.wndMaxConcurrent);
    this.supportPopup = function (url, width, height) {
        var w = window.open(url, 'popup', 'width=' + width + ',height=' + height + ',resizable=yes,scrollbars=yes');
        w.focus();
    };
    var currentIncommingAttackCount = 0;
    this.setIncommingAttackCount = function (num) {
        var elem = $('#incomming_attack_count');
        var elmsize = 42;
        if (num == 0 && currentIncommingAttackCount > 0) {
            elem.animate({
                'height': '0px'
            }, 1000, 'linear', function () {
                elem.css('display', 'none');
            });
            $('#notification_area').animate({
                'bottom': '11px'
            }, 1100, 'linear');
            NotificationStack.setAdditionalReservedHeight(0);
        } else if (num > 0 && currentIncommingAttackCount == 0) {
            $('#notification_area').animate({
                'bottom': '51px'
            }, 400, 'linear');
            elem.css('display', 'block');
            elem.animate({
                'height': '41px'
            }, 1000, 'linear');
            NotificationStack.setAdditionalReservedHeight(40);
        }
        currentIncommingAttackCount = num;
        $('#incomming_attack_count_text').html(currentIncommingAttackCount);
    };
    if (options.onlyInstantiate === true) {
        return;
    }
    var ajaxloader = new gpAjax(this, false);
    this.ajax = function (controller, action, params, show_loader, callback, method) {
        if (!params) {
            params = {
                town_id: Game.townId
            };
        } else if (!params.town_id) {
            params.town_id = Game.townId;
        }
        params.nlreq_id = Layout.getLastRequestedNotificationId();
        if (method == 'get') {
            ajaxloader.tmpGet(controller, action, params, show_loader, function (_layout, data, flags) {
                callback(data);
            });
        } else if (method == 'post') {
            ajaxloader.tmpPost(controller, action, params, show_loader, function (_layout, data, flags) {
                callback(data);
            });
        } else {
            if (method !== undefined) {
                debug('check & remove old 6th param (used for ajax-call-locking) of ajax call!!!');
            }
            ajaxloader.post(controller, action, params, show_loader, function (_layout, data, flags) {
                callback(data);
            });
        }
    };
    this.ajaxGet = function (controller, action, params, show_loader, callback) {
        this.ajax(controller, action, params, show_loader, callback, 'get');
    };
    this.ajaxPost = function (controller, action, params, show_loader, callback) {
        this.ajax(controller, action, params, show_loader, callback, 'post');
    };
    this.hideAllianceForumLink = function () {};
    this.showAllianceForumLink = function () {};
    window.setInterval(function () {
        var bits = this.getBarBitMask();
        this.requestBarSubstreamWithNextNotificationTransmission(bits.LAYOUT_BAR_NEW_FARM_TOWN_ATTACK_REPORT | bits.LAYOUT_BAR_REPORT_STATUS | bits.LAYOUT_BAR_RANKING);
        this.resetNotificationRequestTimeout(2000);
    }.bind(this), 9E5);
    initialize(options.onInit);
}

var MailingList = {
    recipient_list: null,
    init: function () {
        $('#recipient_list a.cancel').mousePopup(new MousePopup('Remove'));
        $('#mailing_list a.cancel').mousePopup(new MousePopup('Delete list'));
    },
    addRecipient: function (list_id, tab_number) {
        var name = $('#recipient_list_form_' + tab_number + ' input').val();
        var params = {
            'recipient_name': name,
            'list_id': list_id,
            'tab_number': tab_number
        };
        Layout.wnd.getOpenFirst(Layout.wnd.TYPE_MESSAGE).requestContentPost('message', 'add_recipient', params);
    },
    removeRecipient: function (recipient_id, list_id) {
        var params = {
            'recipient_id': recipient_id,
            'list_id': list_id
        };
        Layout.wnd.getOpenFirst(Layout.wnd.TYPE_MESSAGE).requestContentPost('message', 'remove_recipient', params);
    },
    createList: function () {
        var params = {
            'list_name': $('#create_list_name_form input').val()
        };
        Layout.wnd.getOpenFirst(Layout.wnd.TYPE_MESSAGE).requestContentPost('message', 'create_list', params);
    },
    removeList: function (id) {
        var params = {
            'list_id': id
        };
        Layout.wnd.getOpenFirst(Layout.wnd.TYPE_MESSAGE).requestContentPost('message', 'remove_list', params);
    },
    editListName: function (number) {
        $('#list_' + number).hide();
        $('#list_edit_' + number).show();
        $('#create_list_button').hide();
        $('#create_list_name_form').hide();
    },
    saveListName: function (id) {
        var list_name = $('#mailing_list_' + id + '_name_input input').val();
        var params = {
            'list_id': id,
            'list_name': list_name
        };
        Layout.wnd.getOpenFirst(Layout.wnd.TYPE_MESSAGE).requestContentPost('message', 'rename_list', params);
    },
    setRecipients: function (elem) {
        var _recipients = [];
        var list_id = $(elem).val();
        if (list_id > 0) {
            $.each(this.recipient_list[list_id].data, function (id, recipient) {
                $.merge(_recipients, [recipient.recipient_name]);
            });
            $('#message_recipients').val(_recipients.join('; '));
        } else {
            $('#message_recipients').val('');
        }
    }
}

function MapData(data) {
    var _chunks = [];
    var towns_cache = {},
        islands_cache = {},
        wonders_cache = {},
        presents_cache = {};
    var that = this;
    this.get = function (x, y) {
        var chunk = WMap.toChunk(x, y);
        var chunkData = _chunks[chunk.chunk.x * WMap.map_size + chunk.chunk.y];
        if (chunkData === undefined || chunkData.tiles === undefined || chunkData.loading === true) {
            return undefined;
        }
        if (chunkData.tiles[chunk.rel.y] === undefined) {
            return undefined;
        }
        return chunkData.tiles[chunk.rel.y][chunk.rel.x];
    };
    this.getChunk = function (x, y) {
        var chunkData = _chunks[x * WMap.map_size + y];
        if (chunkData.loading === true) {
            return undefined;
        }
        return chunkData;
    };
    this.getTownsFromPosForCurrentViewport = function (x, y, width, height) {
        var cachekey = x + '-' + y + '-' + width + '-' + height,
            chunks = this.getCoveredChunks(x, y, width, height),
            result = [],
            dbldetect = {},
            i = chunks.length,
            j, chunk, chunkData, town, pixel, coords, radius = 32,
            key, tx = WMap.mapTiles.tileSize.x,
            ty = WMap.mapTiles.tileSize.y,
            m2p = WMap.mapTiles.map2Pixel;
        if (undefined != towns_cache[cachekey]) {
            return towns_cache[cachekey];
        }
        while (i--) {
            chunk = chunks[i];
            chunkData = _chunks[chunk.x * WMap.map_size + chunk.y];
            if (chunkData == undefined || chunkData.loading === true) {
                continue;
            }
            for (j in chunkData.towns) {
                town = chunkData.towns[j];
                pixel = m2p.call(WMap.mapTiles, town.x, town.y);
                coords = {
                    x: ~~ (pixel.x - WMap.scroll.x - tx + town.ox + 42),
                    y: pixel.y - WMap.scroll.y - ty + town.oy + 35
                };
                if ((coords.x + radius < 0) || (coords.y + radius < 0) || (coords.x - radius > WMap.size.x) || (coords.y - radius > WMap.size.y)) {
                    continue;
                }
                key = WMap.getTownType(town) + town.id;
                if (dbldetect[key]) {
                    continue;
                }
                dbldetect[key] = true;
                result.push(town);
            }
        }
        towns_cache[cachekey] = result;
        return result;
    };

    function getTowns(id) {
        if (id == undefined) {
            return [];
        }
        id = id.toString();
        var i = _chunks.length,
            j, town, towns, chunk, stack = [];
        while (i--) {
            if (!(chunk = _chunks[i])) {
                continue;
            }
            towns = chunk.towns;
            for (j in towns) {
                if ((town = towns[j]).id && town.id === id) {
                    stack.push(town);
                }
            }
        }
        return stack;
    }
    this.getFarmTown = function (id) {
        var stack = getTowns(id),
            town;
        while ((town = stack.pop())) {
            if (town.relation_status || town.expansion_stage) {
                return town;
            }
        }
        return null;
    };
    this.getTown = function (id) {
        var stack = getTowns(id),
            town;
        while ((town = stack.pop())) {
            if (town.points) {
                return town;
            }
        }
        return null;
    };
    this.invalidateTownsCache = function () {
        towns_cache = {};
    };
    this.updateStatus = function (town_id, satisfaction, lootable_at, last_looted_at, lootable_human, relation_status, expansion_stage) {
        var stop_counter = 0;
        var il = _chunks.length;
        while (il--) {
            var chunkData = _chunks[il];
            if (chunkData == undefined || chunkData.loading === true) {
                continue;
            }
            var j;
            for (j in chunkData.towns) {
                if (WMap.getTownType(chunkData.towns[j]) == 'farm_town' && chunkData.towns[j].id == town_id) {
                    chunkData.towns[j].popup = null;
                    if (lootable_at > 0) {
                        chunkData.towns[j].loot = lootable_at;
                        chunkData.towns[j].lootable_human = lootable_human;
                        chunkData.towns[j].looted = last_looted_at;
                    }
                    if (relation_status !== undefined && chunkData.towns[j].relation_status != relation_status) {
                        chunkData.towns[j].relation_status = relation_status;
                    }
                    if (expansion_stage !== undefined && chunkData.towns[j].expansion_stage != expansion_stage) {
                        chunkData.towns[j].expansion_stage = expansion_stage;
                    }
                    if (satisfaction !== undefined && chunkData.towns[j].mood != satisfaction && satisfaction > 0) {
                        chunkData.towns[j].mood = ~~satisfaction;
                    }
                    stop_counter++;
                    if (stop_counter == 4) {
                        return;
                    }
                }
            }
        }
        this.invalidateTownsCache();
    };
    this.updateColors = function (towns, color) {
        var len = towns.length;
        while (len--) {
            this.findTownInChunks(towns[len]).fc = color;
        }
    };
    this.findUnconqueredFarmTownInChunks = function () {
        var i = _chunks.length,
            towns = [],
            town;
        var own = this.findTownInChunks(Game.townId);
        while (i--) {
            var chunkData = _chunks[i];
            if (chunkData == undefined || chunkData.loading === true) {
                continue;
            }
            var j;
            for (j in chunkData.towns) {
                if (WMap.getTownType(chunkData.towns[j]) == 'farm_town' && chunkData.towns[j].relation_status === 0) {
                    town = chunkData.towns[j];
                    if (town.x == own.x && town.y == own.y) {
                        towns.push({
                            id: town.id,
                            ox: town.ox,
                            oy: town.oy
                        });
                    }
                }
            }
        }
        var dist = Infinity;
        var tmp, id;
        var k = towns.length;
        while (k--) {
            town = towns[k];
            tmp = Math.sqrt(Math.pow(town.ox - own.ox, 2) + Math.pow(town.oy - own.oy, 2));
            if (tmp < dist) {
                dist = tmp;
                id = town.id;
            }
        }
        return id;
    };
    this.findTownInChunks = function (town_id, farm_town) {
        var i = _chunks.length,
            chunkData, town;
        while (i--) {
            chunkData = _chunks[i];
            if (chunkData == undefined || chunkData.loading === true) {
                continue;
            }
            var j;
            for (j in chunkData.towns) {
                town = chunkData.towns[j];
                if (town.id == town_id) {
                    if ((farm_town && town.demand)) {
                        return town;
                    }
                    if (!farm_town && !town.demand) {
                        return town;
                    }
                }
            }
        }
        return {};
    };
    this.getWondersFromPosForCurrentViewport = function (x, y, width, height) {
        var cachekey = x + '-' + y + '-' + width + '-' + height;
        if (undefined != wonders_cache[cachekey]) {
            return wonders_cache[cachekey];
        }
        var chunks = this.getCoveredChunks(x, y, width, height),
            result = [];
        var dbldetect = {};
        var i = chunks.length,
            chunk, chunkData, j, wonders, wonder, pixel, coords, radius;
        while (i--) {
            chunk = chunks[i];
            chunkData = _chunks[chunk.x * WMap.map_size + chunk.y];
            if (chunkData == undefined || chunkData.loading === true) {
                continue;
            }
            wonders = chunkData.wonders;
            for (j in wonders) {
                wonder = wonders[j];
                pixel = WMap.mapTiles.map2Pixel(wonder.x, wonder.y);
                coords = {
                    x: Math.round(pixel.x - WMap.scroll.x - WMap.mapTiles.tileSize.x + wonder.ox + 42),
                    y: pixel.y - WMap.scroll.y - WMap.mapTiles.tileSize.y + wonder.oy + 35,
                    radius: 32
                };
                radius = coords.radius;
                if ((coords.x + radius < 0) || (coords.y + radius < 0) || (coords.x - radius > WMap.size.x) || (coords.y - radius > WMap.size.y)) {
                    continue;
                }
                result.push(wonder);
            }
        }
        wonders_cache[cachekey] = result;
        return result;
    };
    this.getPresentsFromPosForCurrentViewport = function (x, y, width, height) {
        var cachekey = x + '-' + y + '-' + width + '-' + height;
        if (undefined != presents_cache[cachekey]) {
            return presents_cache[cachekey];
        }
        var chunks = this.getCoveredChunks(x, y, width, height),
            result = [];
        var dbldetect = {};
        var i = chunks.length,
            chunk, chunkData, j, presents, wonder, pixel, coords, radius;
        while (i--) {
            chunk = chunks[i];
            chunkData = _chunks[chunk.x * WMap.map_size + chunk.y];
            if (chunkData == undefined || chunkData.loading === true) {
                continue;
            }
            presents = chunkData.presents;
            for (j in presents) {
                present = presents[j];
                pixel = WMap.mapTiles.map2Pixel(present.x, present.y);
                coords = {
                    x: Math.round(pixel.x - WMap.scroll.x - WMap.mapTiles.tileSize.x + present.ox + 42),
                    y: pixel.y - WMap.scroll.y - WMap.mapTiles.tileSize.y + present.oy + 35,
                    radius: 32
                };
                radius = coords.radius;
                if ((coords.x + radius < 0) || (coords.y + radius < 0) || (coords.x - radius > WMap.size.x) || (coords.y - radius > WMap.size.y)) {
                    continue;
                }
                result.push(present);
            }
        }
        presents_cache[cachekey] = result;
        return result;
    };
    this.getIslandInfos = function (x, y, width, height) {
        var cachekey = x + '-' + y + '-' + width + '-' + height;
        if (undefined != islands_cache[cachekey]) {
            return islands_cache[cachekey];
        }
        var chunks = this.getCoveredChunks(x, y, width, height),
            result = [];
        var dblcheck = {};
        var tbx = MapTiles.tileBuffer.x;
        var tby = MapTiles.tileBuffer.y;
        var i = chunks.length,
            j, chunk, chunkData, foo, name;
        while (i--) {
            chunk = chunks[i];
            chunkData = _chunks[chunk.x * WMap.map_size + chunk.y];
            if (chunkData == undefined || chunkData.loading === true) {
                continue;
            }
            j = chunkData.islands.length;
            while (j--) {
                foo = chunkData.islands[j];
                if (foo.x >= x - tbx && foo.x <= x + width + tbx && foo.y >= y - tby && foo.y <= y + height + tby) {
                    name = foo.x + '_' + foo.y;
                    if (dblcheck[name]) {
                        continue;
                    }
                    dblcheck[name] = true;
                    result.push(foo);
                }
            }
        }
        islands_cache[cachekey] = result;
        return result;
    };
    this.setData = function (data) {
        var chunks_replaced = 0;
        var il = data.length;
        while (il--) {
            var chunk = data[il];
            chunk.loading = false;
            if (_chunks[chunk.chunk.x * WMap.map_size + chunk.chunk.y] == undefined) {
                _chunks[chunk.chunk.x * WMap.map_size + chunk.chunk.y] = chunk;
            } else if (chunk.chunk.timestamp > _chunks[chunk.chunk.x * WMap.map_size + chunk.chunk.y].chunk.timestamp) {
                _chunks[chunk.chunk.x * WMap.map_size + chunk.chunk.y] = chunk;
                chunks_replaced++;
            }
        }
        return chunks_replaced;
    };
    this.loadData = function (chunks, success_handler) {
        WMap.ajaxloader.tmpGet('map_data', 'get_chunks', chunks, true, function (_Layout, data, flag) {
            var chunks_replaced = this.setData(data);
            if (success_handler) {
                success_handler.call(this, chunks_replaced);
            }
        }.bind(this));
    };
    this.getCoveredChunkRectangle = function (x, y, width, height) {
        var result = {};
        result.minX = Math.max(parseInt(x / WMap.chunkSize, 10), 0);
        result.minY = Math.max(parseInt(y / WMap.chunkSize, 10), 0);
        result.maxX = Math.min(parseInt((x + width) / WMap.chunkSize, 10), WMap.mapTiles.mapSize / WMap.chunkSize - 1);
        result.maxY = Math.min(parseInt((y + height) / WMap.chunkSize, 10), WMap.mapTiles.mapSize / WMap.chunkSize - 1);
        return result;
    };
    this.getCoveredChunks = function (x, y, width, height) {
        var rect = this.getCoveredChunkRectangle(x, y, width, height);
        var chunks = [];
        for (y = rect.minY; y <= rect.maxY; y++) {
            for (x = rect.minX; x <= rect.maxX; x++) {
                chunks.push({
                    x: x,
                    y: y
                });
            }
        }
        return chunks;
    };
    this.checkReload = function (x, y, width, height, success_handler) {
        var chunks = this.getCoveredChunks(x, y, width, height);
        var reload = [];
        var il = chunks.length;
        while (il--) {
            var pos = chunks[il];
            var chunkData = _chunks[pos.x * WMap.map_size + pos.y];
            if (chunkData === undefined) {
                _chunks[pos.x * WMap.map_size + pos.y] = {
                    loading: true,
                    chunk: {
                        timestamp: 0
                    }
                };
                reload.push({
                    x: pos.x,
                    y: pos.y,
                    timestamp: _chunks[pos.x * WMap.map_size + pos.y].chunk.timestamp
                });
            }
        }
        if (reload.length) {
            this.loadData(reload, success_handler);
        } else if (success_handler) {
            success_handler.call();
        }
    };
    this.checkCache = function (x, y, width, height, success_handler) {
        var chunks = this.getCoveredChunks(x, y, width, height);
        var reload = [];
        var il = chunks.length;
        while (il--) {
            var pos = chunks[il];
            var chunkData = _chunks[pos.x * WMap.map_size + pos.y];
            if (chunkData != undefined && chunkData.loading === false) {
                reload.push({
                    x: pos.x,
                    y: pos.y,
                    timestamp: _chunks[pos.x * WMap.map_size + pos.y].chunk.timestamp
                });
            }
        }
        if (reload.length > 0) {
            this.loadData(reload, success_handler);
        } else if (success_handler) {
            success_handler.call();
        }
    };
    that.setData(data);
}

var WMap = {
    scroll_int: 0,
    chunkSize: 20,
    scrolled: false,
    size: {},
    island_id: null,
    sea_id: [-1, -1],
    elm: {},
    position_tile_offset: {},
    town_position: {},
    nice_browser: null,
    marker_offset: {
        x: 45,
        y: 35
    },
    ajaxloader: null,
    map_size: null,
    scroll: {
        'x': 0,
        'y': 0
    },
    currently_scrolling: false,
    jQElm: {},
    mover_pixel_offset: null,
    mousePopup: new MousePopup(),
    lookup_sin: [],
    lookup_cos: [],
    dScroll: {},
    map_arrow_type: '',
    initialize: function (data, islands, current_position, map_size, chunc_size, own_town_position, map_arrow_type) {
        var that = this;
        current_position.island_id = parseInt(current_position.island_id, 10);
        current_position.x = parseInt(current_position.x, 10);
        current_position.y = parseInt(current_position.y, 10);
        this.map_size = map_size;
        this.elm = {
            'mover': $('#map'),
            'marker': $('#map_marker'),
            'wrapper': $('#map_wrapper'),
            'move_container': $('#map_move_container'),
            'coord_popup': $('#mouse_coordinate_popup'),
            'town_d': $('#town_direction'),
            'xcoord': $('#xcoord'),
            'ycoord': $('#ycoord'),
            'sea_id': document.getElementById('sea_id'),
            'duration': $('#map_duration'),
            'd': $(document),
            'popup': $('#popup_div'),
            'popup_content': $('#popup_content')
        };
        this.ajaxloader = new gpAjax(Layout, false);
        this.mover_pixel_offset = this.elm.mover.offset();
        this.mapData = new MapData(data);
        this.mapTiles = MapTiles;
        this.mapX = current_position.x;
        this.mapY = current_position.y;
        this.size = {
            'x': this.elm.wrapper.width(),
            'y': this.elm.wrapper.height()
        };
        this.mapTiles.initialize(this.mapData, this.size.x, this.size.y, islands, map_size);
        this.town_position = own_town_position;
        this.position_tile_offset = {
            x: ~~ (this.size.x / this.mapTiles.tileSize.x),
            y: ~~ (this.size.y / this.mapTiles.tileSize.y)
        };
        this.scrollBorder = this.mapTiles.getScrollBorder();
        this.island_id = current_position.island_id;
        this.town_id = this.town_position.id;
        var town_tile = function () {
                var x, y, precise, ele;
                if ((ele = document.getElementById('town_' + WMap.town_id))) {
                    x = ele.style.left;
                    y = ele.style.top;
                    precise = true;
                } else {
                    var tmp = MapTiles.map2Pixel(WMap.town_position.x, WMap.town_position.y);
                    x = tmp.x;
                    y = tmp.y;
                }
                return {
                    'x': x,
                    'y': y,
                    'precise': precise
                };
            }.call();
        this.town_position.x_px = parseInt(town_tile.x, 10) + this.marker_offset.x;
        this.town_position.y_px = parseInt(town_tile.y, 10) + this.marker_offset.y;
        this.town_position.precise = town_tile.precise;
        this.scrollMapToPos(current_position.x, current_position.y, false, function () {
            Minimap = new GPMinimap('mini', 0.2);
            Picomap = new GPMinimap('pico', 0.125);
            Picomap.fillCanvas();
            that.updateMapCoordInfo(current_position.x - that.position_tile_offset.x + 1, current_position.y - that.position_tile_offset.y + 2);
            that.handler = {};
            that.handler.mouseup = function (e) {
                return WMap.handlerUp(e);
            };
            that.handler.mousemove = function (e) {
                return WMap.handlerMouseMove(e);
            };
            that.handler.mouseMovePositionPopup = function (e) {
                return WMap.mouseMovePositionPopup(e);
            };
            that.elm.mover.mousedown(function (e) {
                return WMap.handlerDown(e);
            });
            $(window).resize(function (e) {
                return WMap.handlerResize(e);
            });
            that.map_arrow_type = map_arrow_type;
            if (!Game.map_arrow_show_always && (!jQuery.support.opacity && !jQuery.support.leadingWhitespace)) {
                that.map_arrow_type = 'none';
            }
            that.determineRenderMode();
            var obj = that.elm.mover[0];
            if (that.map_arrow_type == 'none') {
                that.elm.coord_popup.hide();
            } else {
                try {
                    if (obj.setCapture && obj.attachEvent) {
                        obj.attachEvent('onmousemove', that.handler.mouseMovePositionPopup);
                    } else {
                        document.addEventListener('mousemove', that.handler.mouseMovePositionPopup, true);
                    }
                } catch (e) {}
            }
            var d = Layout.getDocument();
            d.scroll(function () {
                WMap.dScroll = {
                    x: d.scrollLeft(),
                    y: d.scrollTop()
                };
            });
            $('#toggle_color_table').click(WMap.openColorTable).mousePopup(new MousePopup('Color assignments'));
            $('#map_jump_to_current_town_button').click(function () {
                WMap.jumpToPosByCoords();
            }).mousePopup(new MousePopup("Jump to current city"));
            $('#map_tools').keypress(function (e) {
                if (e.keyCode !== 13) {
                    return;
                }
                switch (e.target.id) {
                case 'xcoord':
                    WMap.elm.ycoord.focus();
                    break;
                case 'ycoord':
                    WMap.jumpToPos();
                    break;
                }
            });
            $('#map_jump_button').click(WMap.jumpToPos).mousePopup(new MousePopup('Jump to these coordinates'));
            window.setTimeout(function () {
                var w;
                if (Game.autoOpenTownIndex) {
                    Layout.townIndex.open(Game.townId, Game.townName);
                    if ((w = GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_SHOW_ON_LOGIN))) {
                        w.toTop();
                    }
                }
            }, 150);
            MapBookmarks.init();
        });
        this.elm.mover.bind('mousemove', function (e) {
            WMap.delegateMouseOver(e);
        });
        this.bindNotificationEventHandler();
        this.setupMapUpdateTimer();
    },
    setCurrentTown: function (town_id, island_x, island_y) {
        this.town_id = town_id;
        this.town_position.x = island_x;
        this.town_position.y = island_y;
        var town_tile = function () {
                var x, y, precise, ele, tmp;
                if ((ele = document.getElementById('town_' + WMap.town_id))) {
                    x = ele.style.left;
                    y = ele.style.top;
                    precise = true;
                } else {
                    tmp = MapTiles.map2Pixel(WMap.town_position.x, WMap.town_position.y);
                    x = tmp.x;
                    y = tmp.y;
                }
                return {
                    'x': x,
                    'y': y,
                    'precise': precise
                };
            }.call();
        this.town_position.x_px = parseInt(town_tile.x, 10) + this.marker_offset.x;
        this.town_position.y_px = parseInt(town_tile.y, 10) + this.marker_offset.y;
        this.town_position.precise = town_tile.precise;
    },
    reCenter: function () {
        var diff = {
            'x': this.size.x - this.elm.wrapper.width(),
            'y': this.size.y - this.elm.wrapper.height()
        },
            pos = {
                left: -this.scroll.x - diff.x / 2,
                top: -this.scroll.y - diff.y / 2
            };
        WMap.setMoveContainerPos(pos.left, pos.top, true);
        this.scroll = {
            x: -pos.left,
            y: -pos.top
        };
        this.refresh();
    },
    handlerDown: function (event) {
        var target = event.target || event.srcElement || false;
        if (target && target.tagName.toLowerCase() === 'a') {
            this.mouseDownTarget = target;
        }
        this.currently_scrolling = true;
        this.last_move_x = this.last_move_y = 0;
        this.elm.mover.unbind('mousemove');
        var obj = this.elm.mover[0];
        if (obj.setCapture && obj.detachEvent) {
            obj.setCapture();
            obj.detachEvent('onmousemove', this.handler.mouseMovePositionPopup);
            obj.attachEvent('onmousemove', this.handler.mousemove);
            obj.attachEvent('onmouseup', this.handler.mouseup);
            obj.attachEvent('onlosecapture', this.handler.mouseup);
        } else {
            document.removeEventListener('mousemove', this.handler.mouseMovePositionPopup, true);
            document.addEventListener('mousemove', this.handler.mousemove, true);
            document.addEventListener('mouseup', this.handler.mouseup, true);
        }
        this.elm.coord_popup.hide();
        this.mousemove(event);
    },
    handlerUp: function (e) {
        var d = Layout.getDocument(),
            target = e.target || e.srcElement || false;
        this.currently_scrolling = false;
        if (target && target == this.mouseDownTarget) {
            this.delegateClick(e);
        }
        this.elm.mover.unbind('mousemove mouseleave').bind({
            'mousemove': function (e) {
                WMap.delegateMouseOver(e);
            },
            'mouseleave': function (e) {
                WMap.mousePopup.handlerOut();
            }
        });
        this.mousePopup.handlerOut();
        var obj = this.elm.mover[0];
        if (obj.releaseCapture && obj.detachEvent) {
            obj.detachEvent('onlosecapture', this.handler.mouseup);
            obj.detachEvent('onmouseup', this.handler.mouseup);
            obj.detachEvent('onmousemove', this.handler.mousemove);
            obj.attachEvent('onmousemove', this.handler.mouseMovePositionPopup, true);
            obj.releaseCapture();
        } else {
            d[0].removeEventListener('mouseup', this.handler.mouseup, true);
            d[0].addEventListener('mousemove', this.handler.mouseMovePositionPopup, true);
            d[0].removeEventListener('mousemove', this.handler.mousemove, true);
        }
        this.refresh();
        if (this.map_arrow_type == 'none') {
            this.elm.coord_popup.hide();
        } else {
            this.elm.coord_popup.show();
            this.mouseMovePositionPopup(e);
        }
        if (!this.town_position.precise) {
            var ele;
            if ((ele = d[0].getElementById('town_' + this.town_id))) {
                this.town_position.x_px = parseInt(ele.style.left, 10) + this.marker_offset.x;
                this.town_position.y_px = parseInt(ele.style.top, 10) + this.marker_offset.y;
                this.town_position.precise = true;
            }
        }
        this.dScroll = {
            x: d.scrollLeft(),
            y: d.scrollTop()
        };
    },
    handlerMouseMove: function (e) {
        this.mousemove(e);
    },
    handlerResize: function (e) {
        window.clearTimeout(this.resizeTimeout);
        this.resizeTimeout = window.setTimeout(function () {
            WMap.resize(e);
        }, 300);
    },
    mousemove: function (event, reset_last_move) {
        var viewport_has_changed = false;
        this.scrolled = true;
        if (this.last_move_x === 0 && this.last_move_y === 0 && (reset_last_move === undefined || reset_last_move === true)) {
            this.last_move_x = event.clientX;
            this.last_move_y = event.clientY;
        }
        var diff = {
            x: event.clientX - this.last_move_x,
            y: event.clientY - this.last_move_y
        };
        this.vector = {
            x: diff.x,
            y: diff.y
        };
        this.last_move_x += diff.x;
        this.last_move_y += diff.y;
        this.setScroll(this.scroll.x - (diff.x << 1), this.scroll.y - (diff.y << 1));
        var map = this.mapTiles.pixel2Map(this.scroll.x, this.scroll.y);
        this.mapX = map.x + 1;
        this.mapY = map.y;
        var rowMoveValue, colMoveValue;
        this.updateMapCoordInfo(map.x, map.y);
        while (this.mapX != this.mapTiles.tile.x) {
            colMoveValue = this.mapX < this.mapTiles.tile.x ? 1 : -1;
            this.mapTiles.colMove(colMoveValue);
            viewport_has_changed = true;
        }
        while (this.mapY != this.mapTiles.tile.y) {
            rowMoveValue = this.mapY < this.mapTiles.tile.y ? 1 : -1;
            this.mapTiles.rowMove(rowMoveValue);
            viewport_has_changed = true;
        }
        if (viewport_has_changed) {
            this.mapTiles.updateTownsForCurrentPosition();
        }
        this.setMoveContainerPos(-this.scroll.x, -this.scroll.y);
        if (window.Picomap) {
            Picomap.scrollMapTo2({
                x: -(this.scroll.x + (WMap.mapTiles.xSize)) * Picomap.getScale(),
                y: -(this.scroll.y + (WMap.mapTiles.ySize)) * Picomap.getScale()
            });
        }
        return false;
    },
    resize: function (e) {
        var w = this.elm.wrapper.width(),
            h = this.elm.wrapper.height();
        if (w === this.size.x && h === this.size.y) {
            return;
        }
        if (w > this.size.x || h > this.size.y) {
            this.mapTiles.tileCount = this.mapTiles.getTileCount();
        }
        WMap.reCenter();
        this.size.x = w;
        this.size.y = h;
    },
    mouseMovePositionPopup: function (event) {
        var mouse_pixel_coordinate = {
            x: this.dScroll.x + event.clientX - this.mapPosition.x - this.mover_pixel_offset.left,
            y: this.dScroll.y + event.clientY - this.mapPosition.y - this.mover_pixel_offset.top
        };
        var delta_x = mouse_pixel_coordinate.x - this.town_position.x_px;
        var delta_y = mouse_pixel_coordinate.y - this.town_position.y_px;
        var distance = Math.round(Math.sqrt(Math.pow(delta_x, 2) + Math.pow(delta_y, 2)) * 10) / 10;
        var speed = MapDuration.speed;
        var duration_readable;
        if (speed > 0) {
            var duration_seconds = Math.round((distance * 50) / speed);
            duration_seconds += MapDuration.duration_offset;
            duration_readable = readableSeconds(duration_seconds);
            this.elm.duration[0].innerHTML = '~ ' + duration_readable;
        }
        if (this.map_arrow_type === 'none') {
            return;
        }
        var mouse_map_coordinate = this.mapTiles.pixel2Map(mouse_pixel_coordinate.x + 2, mouse_pixel_coordinate.y + 2);
        mouse_map_coordinate.x += this.mapX;
        mouse_map_coordinate.y += this.mapY;
        var rad = -Math.atan2(delta_x, delta_y) - Math.PI / 2;
        var grad = (!isNaN(rad)) ? Math.round((rad * 180 / Math.PI)) : NaN;
        grad += 270;
        var rotate = '';
        var scale = '';
        var close_to_town = distance < 45;
        if (this.nice_browser && this.map_arrow_type === 'modern') {
            if (!close_to_town) {
                rotate = this.prefix + '-transform:translate(34px,38px) rotate(' + (grad + 90) + 'deg)translate(60px,0);';
            } else {
                rotate = 'display:none;';
            }
            scale = this.prefix + '-transform:scale(1,0.5);';
            this.elm.town_d.attr('style', rotate);
            this.elm.coord_popup.attr('style', scale + 'left:' + (mouse_pixel_coordinate.x - 50) + 'px;top:' + (mouse_pixel_coordinate.y - 50) + 'px;');
        } else if (!this.fixed_direction && this.map_arrow_type == 'modern') {
            this.sin = this.lookup_sin[grad];
            this.cos = this.lookup_cos[grad];
            if (!close_to_town) {
                this.elm.town_d[0].filters.item(0).M11 = this.cos;
                this.elm.town_d[0].filters.item(0).M12 = -this.sin;
                this.elm.town_d[0].filters.item(0).M21 = this.sin;
                this.elm.town_d[0].filters.item(0).M22 = this.cos;
            }
            this.elm.coord_popup.css({
                left: (mouse_pixel_coordinate.x - 50),
                top: (mouse_pixel_coordinate.y - 50)
            });
            this.elm.coord_popup[0].filters.item(0).M11 = 1;
            this.elm.coord_popup[0].filters.item(0).M12 = 0;
            this.elm.coord_popup[0].filters.item(0).M21 = 0;
            this.elm.coord_popup[0].filters.item(0).M22 = 0.5;
            var x_cmp = ~~ ((this.cos * this.t_off_x) - (this.sin * this.t_off_y) + this.p_offset_x - (this.elm.town_d.width() >> 1)),
                y_cmp = ~~ ((this.sin * this.t_off_x) + (this.cos * this.t_off_y) + 70 - (this.elm.town_d.height() >> 1));
            this.elm.town_d_wrapper.attr('style', 'left:' + x_cmp + 'px;top:' + y_cmp + 'px;' + (close_to_town ? 'display:none;' : ''));
        } else {
            this.d = this.d || 'f00';
            if (22.5 <= grad && grad < 67.5) {
                this.d = 'sw';
            } else if (337.5 <= grad || grad < 22.5) {
                this.d = 's';
            } else if (292.5 <= grad && grad < 337.5) {
                this.d = 'se';
            } else if (247.5 <= grad && grad < 292.5) {
                this.d = 'e';
            } else if (202.5 <= grad && grad < 247.5) {
                this.d = 'ne';
            } else if (157.5 <= grad && grad < 202.5) {
                this.d = 'n';
            } else if (112.5 <= grad && grad < 157.5) {
                this.d = 'nw';
            } else if (67.5 <= grad && grad < 112.5) {
                this.d = 'w';
            }
            if (!this.elm.coord_popup.hasClass(this.d)) {
                this.elm.coord_popup.attr('class', this.d);
            }
            this.elm.coord_popup.attr('style', 'left:' + (mouse_pixel_coordinate.x - 50) + 'px;top:' + (mouse_pixel_coordinate.y - 25) + 'px;');
        }
    },
    determineRenderMode: function () {
        var d = Layout.getDocument(),
            version = $.browser.version,
            browser = $.browser,
            i = 360,
            pi = 2 * Math.PI / 360,
            half_pi = Math.PI / 2;
        this.dScroll = {
            x: d.scrollLeft(),
            y: d.scrollTop()
        };
        this.nice_browser = false;
        this.prefix = '';
        while (i--) {
            this.lookup_sin.push(Math.sin(half_pi + pi * -i));
            this.lookup_cos.push(Math.cos(half_pi + pi * -i));
        }
        if (browser.mozilla && version >= '1.9.1') {
            this.prefix = '-moz';
            this.nice_browser = true;
        } else if (browser.webkit && version >= '525') {
            this.prefix = '-webkit';
            this.nice_browser = true;
        } else if (browser.opera && parseFloat(version) >= parseFloat('10.5')) {
            this.prefix = '-o';
            this.nice_browser = true;
        } else if (browser.msie && version >= '9') {
            this.prefix = '-ms';
            this.nice_browser = true;
        } else if (browser.msie && version >= '7') {
            this.sin = null;
            this.cos = null;
            if (version >= '8') {
                this.p_offset_x = 70;
            } else {
                this.p_offset_x = 0;
            }
            this.t_off_x = 60;
            this.t_off_y = 0;
            this.elm.coord_popup.addClass('ie');
            this.elm.town_d_wrapper = $('#town_direction_wrapper');
            this.elm.town_d[0].style.filter = 'progid:DXImageTransform.Microsoft.Matrix(FilterType="bilinear", sizingMethod="auto expand",M11 = ' + this.cos + ', M12 = ' + (-this.sin) + ', M21 = ' + this.sin + ', M22 = ' + this.cos + ');';
            this.elm.coord_popup[0].style.filter = 'progid:DXImageTransform.Microsoft.Matrix(FilterType="bilinear", sizingMethod="auto expand",M11 = 1, M12 = 0, M21 = 0, M22 = 0.5);';
            this.handlerUp({
                clientX: 0,
                clientY: 0
            });
        } else {
            this.elm.coord_popup.empty().attr('id', 'mouse_coordinate_popup_fixed');
            this.fixed_direction = true;
        }
        if (this.map_arrow_type == 'fallback') {
            $('#town_direction_wrapper').hide();
            this.elm.coord_popup.attr('id', 'mouse_coordinate_popup_fixed');
        }
    },
    setMoveContainerPos: function (left, top, animate) {
        var c;
        left -= this.mapTiles.tileSize.x;
        top -= this.mapTiles.tileSize.y;
        if (window.opera) {
            left = left - this.mapTiles.cssOffset.x;
            top = top - this.mapTiles.cssOffset.y;
            while (left > 20000) {
                left -= 20000;
                this.mapTiles.cssOffset.x += 20000;
                this.mapTiles.setAllTilePixel();
            }
            while (left < -20000) {
                left += 20000;
                this.mapTiles.cssOffset.x -= 20000;
                this.mapTiles.setAllTilePixel();
            }
            while (top > 20000) {
                top -= 20000;
                this.mapTiles.cssOffset.y += 20000;
                this.mapTiles.setAllTilePixel();
            }
            while (top < -20000) {
                top += 20000;
                this.mapTiles.cssOffset.y -= 20000;
                this.mapTiles.setAllTilePixel();
            }
        }
        this.mapPosition = {
            x: left,
            y: top
        };
        if (!animate) {
            c = this.elm.move_container[0];
            c.style.left = left + 'px';
            c.style.top = top + 'px';
        } else {
            this.elm.move_container.stop(true, true).animate({
                'left': left,
                'top': top
            }, 750, 'swing');
        }
    },
    setScroll: function (x, y) {
        this.scroll.x = bound(x, this.scrollBorder.xMin, this.scrollBorder.xMax);
        this.scroll.y = bound(y, this.scrollBorder.yMin, this.scrollBorder.yMax);
    },
    setPosition: function (pos) {
        this.mapX = pos.x;
        this.mapY = pos.y;
        var pixel;
        WMap.setMoveContainerPos(-WMap.town_position.x_px + (WMap.elm.wrapper.width() >> 1), -WMap.town_position.y_px + (WMap.elm.wrapper.height() >> 1));
        WMap.scroll = {
            x: WMap.town_position.x_px,
            y: WMap.town_position.y_px
        };
        this.mapTiles.tile = {
            x: this.mapX - 3,
            y: this.mapY - 1
        };
        pixel = this.mapTiles.map2Pixel(this.mapX, this.mapY);
        var offset = {
            'x': 0,
            'y': 0
        },
            island_width_in_tiles = this.mapTiles.islands[this.island_id].width,
            island_width_in_pixels = island_width_in_tiles * this.mapTiles.tileSize.x / 2,
            island_offset_x = this.mapTiles.islands[this.island_id].centering_offset_x,
            island_offset_y = this.mapTiles.islands[this.island_id].centering_offset_y;
        offset.x = (island_width_in_pixels >> 1) - island_offset_x;
        offset.y = island_offset_y;
        if (pos.x % 2 == 1) {
            offset.y += this.mapTiles.tileSize.y;
        }
        pixel.x += (offset.x - this.elm.wrapper.width() >> 1);
        pixel.y += (offset.y - this.elm.wrapper.height() >> 2);
        this.setMoveContainerPos(-pixel.x, -pixel.y);
        this.scroll = {
            x: pixel.x,
            y: pixel.y
        };
        this.refresh();
    },
    refresh: function (type) {
        WMap.mapTiles.updateTownsForCurrentPosition();
        var towns = this.mapData.getTownsFromPosForCurrentViewport(this.mapTiles.tile.x, this.mapTiles.tile.y, this.mapTiles.tileCount.x, this.mapTiles.tileCount.y);
        if (type !== 'towns') {
            this.mapTiles.recreate();
        }
        FarmTownBars.setVisibleTowns(towns);
        this.initFarmTownTimer(towns);
    },
    toChunk: function (x, y) {
        var chunk = {
            x: ~~ (x / this.chunkSize),
            y: ~~ (y / this.chunkSize)
        };
        var rel = {
            x: x % this.chunkSize,
            y: y % this.chunkSize
        };
        return {
            chunk: chunk,
            rel: rel
        };
    },
    scrollMapToPos: function (x, y, check_reload, callback) {
        if (!check_reload) {
            this.mapData.checkReload(x, y, this.chunkSize, this.chunkSize, function () {
                this.setPosition({
                    x: x,
                    y: y
                });
                if (callback) {
                    callback();
                }
            }.bind(this, x, y));
        } else {
            this.setPosition({
                x: x,
                y: y
            });
            if (callback) {
                callback();
            }
        }
        this.last_move_x = this.last_move_y = 0;
    },
    delegateClick: function (e) {
        var data, town, base64, chunkTown, target = this.mouseDownTarget;
        base64 = target.href.split(/#/);
        data = $.parseJSON(atob(base64[1] || base64[0]));
        switch (data.tp) {
        case 'free':
            if (data.inv_spo) {
                if (!GPWindowMgr.getOpenFirst(Layout.wnd.TYPE_INVITE_FRIENDS)) {
                    Layout.contextMenu(e, 'invite_to_colo_flag', data);
                } else {
                    WndHandlerInviteFriends.selectSpotOnMap(data);
                }
            } else {
                Layout.wnd.Create(Layout.wnd.TYPE_UNINHABITED_PLACE, 'Found a new city', {}, {
                    'target_x': data.ix,
                    'target_y': data.iy,
                    'target_number_on_island': data.nr
                });
            }
            break;
        case 'inv_spo':
            WndHandlerInviteFriends.selectSpotOnMap(data);
            break;
        case 'town':
            chunkTown = this.mapData.getTown(data.id);
            data.name = chunkTown.name;
            if (chunkTown.player_name === '') {
                Layout.contextMenu(e, 'ghost_town', data);
            } else {
                Layout.contextMenu(e, 'determine', data);
            }
            break;
        case 'farm_town':
            if (data.ix === this.town_position.x && data.iy === this.town_position.y) {
                town = this.mapData.getFarmTown(data.id);
                if (town.relation_status === 0) {
                    Layout.wnd.Create(Layout.wnd.TYPE_FARM_TOWN, 'Attack on' + town.name, {
                        "action": "attack"
                    }, town.id);
                } else {
                    Layout.contextMenu(e, 'farm_town', town);
                }
            }
            break;
        case 'wonder':
            Layout.wnd.Create(Layout.wnd.TYPE_WONDERS, target.title || this.mousePopup.titleTag || '', {}, data.ix, data.iy);
            break;
        case 'present':
            if (data.pumpkin_hunting) {
                MapPresents.collect(data.ix, data.iy);
            } else {
                Layout.wnd.Create(Layout.wnd.TYPE_PRESENTS, 'Pumpkin found', {}, data.ix, data.iy);
            }
            break;
        case 'island':
            Layout.wnd.Create(Layout.wnd.TYPE_ISLAND, '', {}, data);
            break;
        }
    },
    delegateMouseOver: function (e) {
        var target = e.target,
            town, data, tp, popup, title, base64;
        if (this.currently_scrolling) {
            return null;
        }
        if (!target.href) {
            if (this.mousePopup.titleTag) {
                this.mouseOverTarget.title = this.mousePopup.titleTag;
                delete this.mousePopup.titleTag;
            }
            this.mouseOverTarget = null;
            if (this.mousePopup.enabled) {
                this.mousePopup.handlerOut();
            }
            return this.mousePopup.disable();
        }
        if (this.mouseOverTarget != target) {
            this.mousePopup.initialize();
            this.mouseOverTarget = target;
            base64 = target.href.split(/#/);
            data = $.parseJSON(atob(base64[1] || base64[0]));
            if ((tp = data.tp) === 'island') {
                return null;
            }
            town = (tp === 'town' || tp === 'ghost_town') ? this.mapData.getTown(data.id) : this.mapData.getFarmTown(data.id);
            title = target.title;
            if (title) {
                this.mousePopup.titleTag = title;
            }
            target.title = '';
            popup = title || this.createTownPopup(data.tp, town || data.inv_spo);
            if (popup) {
                this.mousePopup.enable();
            } else {
                this.mousePopup.disable();
            }
            this.mousePopup.xhtml = popup;
            this.mousePopup.showDiv();
        }
        this.mousePopup.handlerMove(e);
    },
    initFarmTownTimer: function (towns) {
        var next_load_ready;
        var next_farm_town_id;
        var now = Timestamp.now();
        var il = towns.length;
        while (il--) {
            var town_type = this.getTownType(towns[il]);
            if (town_type == 'farm_town' && towns[il].relation_status == 1 && towns[il].loot > now) {
                if (next_load_ready == undefined) {
                    next_load_ready = towns[il].loot;
                    next_farm_town_id = towns[il].id;
                } else {
                    if (towns[il].loot < next_load_ready) {
                        next_load_ready = towns[il].loot;
                        next_farm_town_id = towns[il].id;
                    }
                }
            }
        }
        if (WMap.farm_town_timer) {
            delete WMap.farm_town_timer;
        }
        if (next_load_ready !== undefined) {
            var time = (next_load_ready - now) * 1000;
            WMap.farm_town_timer = window.setTimeout(function () {
                WMap.loadReady(next_farm_town_id, towns);
            }, time);
        }
    },
    loadReady: function (farm_town_id, towns) {
        WMap.showLoadIcon(farm_town_id);
        delete WMap.farm_town_timer;
        WMap.initFarmTownTimer(towns);
    },
    updateStatusInChunkTowns: function (town_id, satisfaction, lootable_at, last_looted_at, lootable_human, relation_status, expansion_stage) {
        this.mapData.updateStatus(town_id, satisfaction, lootable_at, last_looted_at, lootable_human, relation_status, expansion_stage);
    },
    createTownPopup: function (town_type, town__or__invitation_spot) {
        var html;
        switch (town_type) {
        case 'town':
            normalTown(town__or__invitation_spot);
            break;
        case 'farm_town':
            farmTown(town__or__invitation_spot);
            break;
        case 'wonder':
            wonderPopup();
            break;
        case 'inv_spo':
            inviteFriend();
            break;
        case 'free':
            if (town__or__invitation_spot) {
                foundTownOrInviteFriend();
            } else {
                foundTown();
            }
            break;
        default:
            foundTown();
        }

        function normalTown(town) {
            html = '<h4>' + town.name + '</h4>' + '<table class="popup_table_inside">' + '<tr><td>' + '<img src="http://cdn.grepolis.com/images/game/temp/player.png" />' + '</td><td>' + town.player_name + '</td></tr>' + '<tr><td>' + '<img src="http://cdn.grepolis.com/images/game/temp/points.png" />' + '</td><td>' + ngettext('%1 point', new Array('%1 point', '%1 points'), town.points).replace('%1', town.points) + '</td></tr>';
            if (town.alliance_name != null) {
                html += '<tr><td>' + '<img src="http://cdn.grepolis.com/images/game/temp/ally.png" />' + '</td><td>' + town.alliance_name + '</td></tr>';
            }
            html += '</table>';
        }

        function farmTown(town) {
            if (town.x == WMap.town_position.x && town.y == WMap.town_position.y) {
                switch (town.relation_status) {
                case 0:
                    town.player_relation_message = 'Not conquered';
                    break;
                case 1:
                    town.player_relation_message = 'In your possession';
                    break;
                case 2:
                    town.player_relation_message = 'There is a revolt';
                    break;
                }
                if (!town.loot) {
                    town.loot = null;
                }
                html = tmpl2(GameData.FarmMouseOverTemplate, town);
            } else {
                html = null;
            }
        }

        function wonderPopup() {
            html = 'Construction site for a World Wonder';
        }

        function foundTown() {
            html = 'Here you can found a new city';
        }

        function inviteFriend() {
            html = 'Invite a friend to build a city here';
        }

        function foundTownOrInviteFriend() {
            html = 'Invite a friend to build a city here<br/>or found a new city';
        }
        return html;
    },
    notificationEventHandler: function (event, notification) {
        switch (event.type) {
        case 'notification.setTownName':
            WMap.pollForMapChunksUpdate();
            break;
        case 'notification.farmConquered':
            var farm_town_id = ~~notification.param_str;
            WMap.updateStatusInChunkTowns(farm_town_id, -1, -1, -1, '', 1);
            WMap.changeFarmTownFlag(farm_town_id, 1);
            WMap.pollForMapChunksUpdate();
            break;
        default:
            break;
        }
    },
    bindNotificationEventHandler: function () {
        Layout.getDocument().bind({
            'notification.setTownName': WMap.notificationEventHandler,
            'notification.farmConquered': WMap.notificationEventHandler
        });
    },
    getSea: function (x, y) {
        var tmp = 10 / this.map_size;
        var sea_x = ~~ (x * tmp);
        var sea_y = ~~ (y * tmp);
        return [sea_x, sea_y];
    },
    getCoordsFromSea: function (sea) {
        var sea_y = sea % 10;
        var sea_x = Math.floor(sea / 10);
        var coord_x = Math.round((sea_x * MapTiles.mapSize) / 10);
        var coord_y = Math.round((sea_y * MapTiles.mapSize) / 10);
        return {
            x: coord_x,
            y: coord_y
        };
    },
    updateMapCoordInfo: function (x, y) {
        x += this.position_tile_offset.x - 1;
        y += this.position_tile_offset.y - 2;
        this.elm.xcoord[0].value = x;
        this.elm.ycoord[0].value = y;
        var tmp = this.getSea(x, y);
        if (!(tmp[0] == this.sea_id[0] && tmp[1] == this.sea_id[1])) {
            this.sea_id = tmp;
            this.elm.sea_id.innerHTML = this.sea_id.join('');
            tmp = null;
        }
    },
    getTownType: function (town) {
        if (town.type == 'free') {
            return 'free';
        } else if (town.type == 'inv_spo') {
            return 'inv_spo';
        } else if (town.expansion_stage === undefined) {
            return 'town';
        } else {
            return 'farm_town';
        }
    },
    jumpToPos: function () {
        WMap.jumpToPosByCoords(WMap.elm.xcoord[0].value, WMap.elm.ycoord[0].value);
    },
    jumpToPosByCoords: function (x, y, dontCloseOpenWindows) {
        if (!((Tutorial && Tutorial.isActive()) || dontCloseOpenWindows)) {
            Layout.wnd.closeAll();
        }
        var map = this.mapTiles.pixel2Map(this.scrollBorder.xMax, this.scrollBorder.yMax),
            town, coord_x, coord_y;
        if (x && y) {
            coord_x = bound((~~x), 0, map.x);
            coord_y = bound((~~y), 0, map.y);
        } else {
            coord_x = (town = ITowns.getTown(Game.townId)).island_x;
            coord_y = town.island_y;
        }
        this.scrollMapToPos(coord_x, coord_y, false, function () {
            Layout.getDocument().trigger(GPEvents.global.map.mapjump);
        });
        this.updateMapCoordInfo(coord_x - this.position_tile_offset.x + 1, coord_y - this.position_tile_offset.y + 2);
        return false;
    },
    openColorTable: function () {
        Layout.wnd.Create(Layout.wnd.TYPE_COLOR_TABLE, 'Colors', {});
    },
    findUnconqueredFarmTownId: function () {
        return this.mapData.findUnconqueredFarmTownInChunks();
    },
    setupMapUpdateTimer: function () {
        if (WMap.mapUpdateIntervalTimerHandle != undefined) {
            window.clearInterval(WMap.mapUpdateIntervalTimerHandle);
        }
        WMap.mapUpdateIntervalTimerHandle = window.setInterval(WMap.pollForMapChunksUpdate, Game.map_chunks_poll_time);
    },
    pollForMapChunksUpdate: function () {
        var size = WMap.mapTiles.pixel2Map(WMap.size.x, WMap.size.y);
        WMap.mapData.checkCache(WMap.mapX, WMap.mapY, size.x, size.y, function (chunks_reloaded) {
            if (chunks_reloaded != undefined && chunks_reloaded > 0) {
                WMap.mapData.invalidateTownsCache();
                WMap.refresh();
                Picomap.refresh();
            }
        });
    },
    benchmark: function () {
        var times = 150;
        $(document).everyTime(10, function (i) {
            WMap.last_move_x = 10;
            WMap.last_move_y = 10;
            WMap.mousemove({
                clientX: 15,
                clientY: 15
            }, true);
        }, times);
    },
    showLoadIcon: function (farm_town_id) {
        $('#farm_town_' + farm_town_id).html('<div class="res_available"></div>');
    },
    hideLoadIcon: function (farm_town_id) {
        $('#farm_town_' + farm_town_id + ' div.res_available').remove();
        this.refresh('towns');
    },
    changeFarmTownFlag: function (farm_town_id, status) {
        var img_name = $('#farm_town_' + farm_town_id).css('background-image').replace('.png)', '');
        img_name = img_name.slice(0, img_name.length - 1) + status + '.png)';
        $('#farm_town_' + farm_town_id).css('background-image', img_name);
    }
};
var MapDuration = {
    displayed: false,
    selected: [],
    speed: 0,
    duration_offset: 0,
    naval_speed_bonus_factor: 1,
    calculateSpeedOfUnits: function () {
        this.speed = 0;
        var i = this.selected.length,
            unit_data;
        while (i--) {
            unit_data = GameData.units[this.selected[i]];
            if (this.speed === 0 || this.speed > unit_data.speed) {
                this.speed = unit_data.speed * this.naval_speed_bonus_factor;
            }
        }
        return this.speed;
    },
    selectUnit: function (unit_type, ele) {
        $(ele).toggleClass('selected');
        var i = this.selected.length,
            was_selected = false;
        while (i--) {
            if (this.selected[i] === unit_type) {
                this.selected.remove(i);
                was_selected = true;
            }
        }
        if (!was_selected) {
            this.selected.push(unit_type);
        }
        if (!this.selected.length) {
            WMap.elm.duration.slideUp();
        } else {
            WMap.elm.duration.slideDown();
        }
        this.calculateSpeedOfUnits();
    },
    clear: function () {
        this.selected = [];
        WMap.elm.duration.slideUp();
    }
};
var MapPresents = {
    pumpkin_hunting: false,
    opened: [],
    init: function (data) {
        if (data.pumpkin_hunting) {
            MapPresents.pumpkin_hunting = true;
            MapPresents.startHunting(data.hunting_ends_at);
            $('#presents_count_popup .pumpkin_count').text(data.pumpkin_count);
            MapTiles.updateTownsForCurrentPosition();
        }
    },
    startHunting: function (hunting_ends_at) {
        MapPresents.pumpkin_hunting = true;
        $('#presents_count_popup').show();
        $('#presents_count_popup .counter').countdown(hunting_ends_at, {}).bind('finish', MapPresents.huntingFinished);
    },
    collect: function (ix, iy) {
        MapPresents.opened.push(ix + '_' + iy);
        WMap.ajaxloader.tmpPost('presents', 'collect', {
            island_x: ix,
            island_y: iy
        }, true, function (wnd, data) {
            $('#presents_count_popup .pumpkin_count').text(data.pumpkin_count);
            if (data.pumpkin_count >= 10) {
                MapPresents.huntingFinished();
            }
        });
    },
    huntingFinished: function () {
        MapPresents.pumpkin_hunting = false;
        MapTiles.updateTownsForCurrentPosition();
        Layout.wnd.Create(Layout.wnd.TYPE_PRESENTS, 'Pumpkin found', {}, 0, 0);
        $('#presents_count_popup').hide();
    }
};

var MapTiles = {
    mapSize: null,
    tileSize: {
        x: 256,
        y: 128
    },
    focussed_town_id: null,
    elm: {},
    wonder_stages: [-1, 0, 1, 1, 2, 2, 3, 4, 5, 6, 7],
    tile: {
        'x': 0,
        'y': 0
    },
    queue: [],
    d: document,
    ftColors: ['910e08', '516b2e', 'dc4200'],
    ftOffset: {
        'x': 18,
        'y': 18
    },
    tOffset: {
        'nw': {
            'x': 9,
            'y': 14
        },
        'ne': {
            'x': 17,
            'y': 11
        },
        'sw': {
            'x': 10,
            'y': 13
        },
        'se': {
            'x': 15,
            'y': 13
        }
    },
    debug: {
        'show_coords_on_map': false,
        'map_use_weird_coords': false
    },
    initialize: function (mapData, xSize, ySize, islands, map_size) {
        this.elm = {
            'tiles': $('#map_tiles'),
            'towns': $('#map_towns'),
            'islands': $('#map_islands'),
            'wonders': $('#map_wonders'),
            'presents': $('#map_presents')
        };
        this.mapSize = map_size;
        this.mapData = mapData;
        this.islands = islands;
        this.focussed_town_id = 0;
        this.xSize = xSize;
        this.ySize = ySize;
        this.tileBuffer = {
            x: 2,
            y: 2
        };
        this.tileCount = this.getTileCount();
        this.cssOffset = {
            x: 0,
            y: 0
        };
        $(document).bind(GPEvents.global.town.town_switch, function () {
            WMap.mapTiles.highlightTown();
        });
    },
    getTileCount: function () {
        return {
            x: Math.floor(WMap.elm.wrapper.width() / (this.tileSize.x >> 1)) + this.tileBuffer.x,
            y: Math.floor(WMap.elm.wrapper.height() / (this.tileSize.y >> 1)) + this.tileBuffer.y
        };
    },
    getScrollBorder: function () {
        return {
            xMin: -((this.tileSize.x / 2) + 180),
            yMin: -this.tileSize.y / 2,
            xMax: this.tileSize.x / 2 * (this.mapSize - this.tileCount.x),
            yMax: this.tileSize.y * (this.mapSize - this.tileCount.y)
        };
    },
    map2Pixel: function (x, y) {
        x = +x;
        y = +y;
        var top = this.tileSize.y,
            left = this.tileSize.x >> 1;
        if (x & 1) {
            y += 0.5;
        }
        top *= y;
        left *= x;
        return {
            x: left,
            y: top
        };
    },
    pixel2Map: function (x, y) {
        var mapX = x / (this.tileSize.x >> 1),
            mapY = ((mapX & 1) ? (y - (this.tileSize.y >> 1)) : y) / this.tileSize.y;
        return {
            'x': Math.floor(mapX),
            'y': Math.floor(mapY)
        };
    },
    getImage: function (x, y) {
        var data = this.mapData.get(x, y),
            island, offset, left, top;
        if (data === undefined) {
            return {
                img: 'watertiles.png',
                left: 0,
                top: 0,
                undef: true
            };
        }
        if (!(data >> 8)) {
            left = ((data & 255) % 4) * this.tileSize.x;
            top = ((data & 255) >> 2) * this.tileSize.y;
            return {
                img: 'watertiles.png',
                left: left,
                top: top
            };
        } else {
            island = this.islands[data >> 8];
            offset = this.map2Pixel((data & 255) % (island.width), parseInt((data & 255) / island.width, 10));
            return {
                img: island.img,
                left: offset.x,
                top: offset.y,
                isIsland: true
            };
        }
    },
    colMove: function (dir) {
        var tcx = this.tileCount.x,
            tcy = this.tileCount.y,
            tx = this.tile.x,
            ty = this.tile.y,
            tile, y;
        var oldOffset = (dir == 1) ? tcx - 1 : 0;
        var newOffset = (dir == 1) ? -1 : tcx;
        for (y = 0; y < tcy; y++) {
            tile = this.$(oldOffset + tx, y + ty, true);
            this.setXY(tile, newOffset + tx, y + ty);
        }
        this.tile.x += -dir;
        this.mapData.checkReload(tx, ty, tcx, tcy, this.checkQueue());
    },
    rowMove: function (dir) {
        var tcx = this.tileCount.x,
            tcy = this.tileCount.y,
            tx = this.tile.x,
            ty = this.tile.y,
            tile, x;
        var oldOffset = (dir == 1) ? tcy - 1 : 0;
        var newOffset = (dir == 1) ? -1 : tcy;
        for (x = 0; x < tcx; x++) {
            tile = this.$(x + tx, oldOffset + ty, true);
            this.setXY(tile, x + tx, newOffset + ty);
        }
        this.tile.y += -dir;
        this.mapData.checkReload(tx, ty, tcx, tcy, this.checkQueue());
    },
    setTilePixel: function (tile, x, y) {
        var pixel = this.map2Pixel(x, y);
        tile.style.left = pixel.x + this.cssOffset.x + 'px';
        tile.style.top = pixel.y + this.cssOffset.y + 'px';
    },
    setAllTilePixel: function () {
        $('div.tile').each(function (i, tile) {
            var id = $(tile).attr('id'),
                tmp = id.split('_'),
                x = parseInt(tmp[1], 10),
                y = parseInt(tmp[2], 10);
            if (!isNaN(x) && !isNaN(y)) {
                this.setTilePixel(tile, x, y);
            }
        }.bind(this));
    },
    recreate: function () {
        var e = this.elm,
            container = $('#map_move_container').detach();
        e.tiles[0].innerHTML = '';
        e.tiles[0].innerText = '';
        e.towns[0].innerHTML = '';
        e.islands[0].innerHTML = '';
        e.wonders[0].innerHTML = '';
        e.presents[0].innerHTML = '';
        this.updateTownsForCurrentPosition();
        var fragment = document.createDocumentFragment();
        var y = -this.tileBuffer.y,
            x = -this.tileBuffer.x,
            tcy = this.tileCount.y,
            tcx = this.tileCount.x;
        while (y++ < tcy) {
            while (x++ < tcx) {
                this.addTile(x, y, fragment);
            }
            x = -this.tileBuffer.x;
        }
        e.tiles[0].appendChild(fragment);
        container.appendTo($('#map'));
    },
    addTile: function (x, y, fragment) {
        var tile = document.createElement('div');
        tile.className = 'tile';
        this.setXY(tile, x + this.tile.x, y + this.tile.y);
        fragment.appendChild(tile);
    },
    setXY: function (tile, x, y) {
        var parent = false;
        if (!tile) {
            return;
        }
        if ((parent = tile.parentNode)) {
            parent.removeChild(tile);
        }
        tile.id = 'tile_' + x + '_' + y;
        this.setTilePixel(tile, x, y);
        this.updateTile(tile, x, y);
        if (parent) {
            parent.appendChild(tile);
        }
    },
    updateTile: function (tile, x, y) {
        var image = this.getImage(x, y);
        if (image.undef) {
            this.queue.push({
                'tile': tile,
                'x': x,
                'y': y
            });
            if (!this.queueInterval) {
                this.queueInterval = window.setInterval(function () {
                    WMap.mapTiles.checkQueue();
                }, 50);
            }
        }
        this.setBackground(tile, image, x, y);
    },
    setBackground: function (tile, image, x, y) {
        tile.style.backgroundImage = 'url(http://cdn.grepolis.com/images/game/map/' + image.img + ')';
        tile.style.backgroundPosition = -image.left + 'px ' + -image.top + 'px';
        if (this.debug.show_coords_on_map) {
            tile.innerHTML = x + '|' + y;
        }
    },
    checkQueue: function () {
        if (!this.queue.length) {
            window.clearInterval(this.queueInterval);
            delete this.queueInterval;
            return;
        }
        var ele, image, i = this.queue.length;
        while (i--) {
            ele = this.queue[i];
            image = this.getImage(ele.x, ele.y);
            if (!image.undef) {
                this.setBackground(ele.tile, image);
                this.queue.splice(i, 1);
            }
        }
    },
    updateTownsForCurrentPosition: function () {
        var rect = [this.tile.x, this.tile.y, this.tileCount.x, this.tileCount.y];
        this.updateTowns(rect);
        this.updateWonders(rect);
        this.updatePresents(rect);
        this.updateIslandInfos(rect);
    },
    highlightTown: function (pos) {
        var highlight_type = 'active',
            islandOffset, tOffset, town;
        if (arguments.length != 1) {
            town = WMap.mapData.findTownInChunks(Game.townId);
            if (isEmpty(town)) {
                this.removeHighlight2(highlight_type);
                return;
            }
            islandOffset = this.map2Pixel(town.x, town.y);
            tOffset = this.tOffset[town.dir];
            pos = {
                x: this.cssOffset.x + islandOffset.x + town.ox + tOffset.x,
                y: this.cssOffset.y + islandOffset.y + town.oy + tOffset.y
            };
        }
        pos.x -= 3;
        pos.y += 8;
        this.highlight2(highlight_type, pos);
    },
    focusTown: function (id) {
        var town = WMap.mapData.findTownInChunks(id);
        var highlight_type = 'focussed';
        if (isEmpty(town)) {
            this.removeHighlight2(highlight_type);
            return;
        }
        this.focussed_town_id = id;
        var islandOffset = this.map2Pixel(town.x, town.y);
        var pos = {
            x: this.cssOffset.x + islandOffset.x + town.ox + 32,
            y: this.cssOffset.y + islandOffset.y + town.oy - 20
        };
        this.highlight2(highlight_type, pos);
    },
    removeHighlight2: function (type) {
        var focus = document.getElementById(type + 'town');
        if (focus) {
            $(focus).remove();
        }
    },
    highlight2: function (type, pos) {
        var focus = document.getElementById(type + 'town');
        if (!focus) {
            focus = document.createElement('div');
        }
        focus.id = type + 'town';
        focus.style.left = pos.x + 'px';
        focus.style.top = pos.y + 'px';
        this.elm.towns[0].appendChild(focus);
    },
    createTownDiv: function (town) {
        town.id += '';
        town.id = town.id.replace('=', '');
        var result = [];
        var islandOffset = this.map2Pixel(town.x, town.y);
        var town_type = WMap.getTownType(town);
        var ghost_town = town.player_name === '';
        var id = town_type + '_' + town.id;
        if (document.getElementById(id)) {
            return false;
        }
        var offsetX = this.cssOffset.x + islandOffset.x;
        var offsetY = this.cssOffset.y + islandOffset.y;
        var tOffset = this.tOffset[town.dir] || this.ftOffset;
        var tl = offsetX + town.ox,
            tt = offsetY + town.oy;
        if (tOffset) {
            tl += tOffset.x;
            tt += tOffset.y;
        }
        var link = this.d.createElement('a');
        link.className = 'tile';
        link.href = '#' + btoa('{"id":"' + town.id + '","ix":' + town.x + ',"iy":' + town.y + ',"tp":"' + town_type + '"' + (town_type === 'free' ? (town.invitation_spot ? ',"inv_spo":true' : '') + ',"nr":' + town.nr : '') + (town.distance !== undefined && town.distance !== null ? ',"distance":' + town.distance : '') + (town.player_town_id !== undefined && town.player_town_id !== null ? ',"player_town_id":' + town.player_town_id : '') + (town.nr !== undefined && town.nr !== null ? ',"number_on_island":' + town.nr : '') + '}');
        if (town_type === 'farm_town') {
            var wrapper = this.d.createElement('div');
            wrapper.className = 'tile' + (town.relation_status == 1 ? ' farmtown_owned' : '');
            wrapper.appendChild(link);
            wrapper.style.left = tl + 'px';
            wrapper.style.top = tt + 'px';
            wrapper.id = id;
        } else {
            link.style.left = tl + 'px';
            link.style.top = tt + 'px';
            link.id = id;
        }
        result.push(wrapper || link);
        var left = offsetX + ~~ (town.ox) + ~~ (town.fx),
            top = offsetY + ~~ (town.oy) + ~~ (town.fy),
            flag, flagpole, ftr = town.relation_status,
            color = (town.fc || this.ftColors[ftr]);
        if (town.invitation_spot || town_type === 'town' || town_type === 'farm_town') {
            flagpole = this.d.createElement('div');
            flagpole.className = 'flagpole ' + town_type + ' ' + (ghost_town ? 'ghost_town' : '');
            if (town_type === 'farm_town') {
                left += 24;
                top += 18;
            }
        }
        if (flagpole) {
            flag = this.d.createElement('div');
            flag.id = town_type + '_flag_' + town.id;
            flag.className = 'flag ' + (town.css_class || !isNaN(ftr) ? town_type + ' ftr_' + ftr : town_type);
            if (color) {
                flag.style.backgroundColor = '#' + color;
            } else if (town_type === 'farm_town') {
                flag.style.backgroundColor = '#' + this.ftColors[0];
            }
            flag.style.left = left + 'px';
            flag.style.top = top + 'px';
            if (town.flag_type) {
                flag.style.backgroundImage = 'url(http://cdn.grepolis.com/images/game/flags/map/flag' + town.flag_type + '.png)';
            }
            flag.appendChild(flagpole);
            result.push(flag);
        }
        if (town.invitation_spot || town_type === 'town' || town_type === 'inv_spo') {
            link.className += ' ' + town.dir + ' lvl' + ((town.invitation_spot || town_type === 'inv_spo') ? '0' : town.size);
            if (town.id == WMap.town_id && !WMap.town_position.precise) {
                WMap.town_position.x_px = tl + WMap.marker_offset.x;
                WMap.town_position.y_px = tt + WMap.marker_offset.y;
                WMap.town_position.precise = true;
            }
            if (Game.townId == town.id) {
                this.highlightTown({
                    x: tl,
                    y: tt
                });
            }
            if (town.id == this.focussed_town_id) {
                this.focusTown(town.id);
            }
        } else if (town_type === 'free') {
            link.className += ' found';
        } else if (wrapper) {
            wrapper.className += ' ft' + ((town.id + town.ox * 23 + town.oy * 211) % 5 + 1);
            if (town.loot && parseInt(town.loot, 10) <= Timestamp.now()) {
                wrapper.innerHTML += ('<span class="res_available"></span>');
            }
        }
        return result;
    },
    createWonderDiv: function (wonder) {
        var islandOffset = this.map2Pixel(wonder.ix, wonder.iy);
        var div_wonder = $('<a />').attr('id', 'wonder_' + wonder.ix + '_' + wonder.iy).attr('title', wonder.pop || 'Construction site for a World Wonder').attr('href', '#' + btoa('{"ix":' + wonder.ix + ',"iy":' + wonder.iy + ',"tp":"wonder"}')).addClass('tile').css({
            'left': this.cssOffset.x + islandOffset.x + wonder.ox + 'px',
            'top': this.cssOffset.y + islandOffset.y + wonder.oy + 'px'
        });
        var filename;
        if (wonder.wt != 'wbg' && wonder.wt != null && this.wonder_stages[wonder.stg] >= 0) {
            filename = 'wonder_' + wonder.wt + '.png';
            div_wonder.css({
                'backgroundImage': 'url(http://cdn.grepolis.com/images/game/map/' + filename + ')',
                'width': '99px',
                'background-position': '-' + (this.wonder_stages[wonder.stg] * 99) + 'px 0px'
            });
        } else {
            div_wonder.css('backgroundImage', 'url(http://cdn.grepolis.com/images/game/map/wonder_building_spot.png)');
        }
        return div_wonder[0];
    },
    createPresentDiv: function (present) {
        var a, id = 'present_' + present.ix + '_' + present.iy;
        if (document.getElementById(id)) {
            return false;
        }
        var islandOffset = this.map2Pixel(present.ix, present.iy);
        a = document.createElement('a');
        a.id = id;
        a.className = 'tile';
        a.style.left = this.cssOffset.x + islandOffset.x + present.ox + 'px';
        a.style.top = this.cssOffset.y + islandOffset.y + present.oy + 'px';
        a.style.backgroundImage = 'url(http://cdn.grepolis.com/images/game/map/presents/' + present.img_o + ')';
        if (!(present.open || $.inArray(present.ix + '_' + present.iy, MapPresents.opened) > -1)) {
            a.href = '#' + btoa('{"ix":' + present.ix + ',"iy":' + present.iy + ',"tp":"present","pumpkin_hunting":' + MapPresents.pumpkin_hunting + '}');
            a.title = present.t;
            a.style.backgroundImage = 'url(http://cdn.grepolis.com/images/game/map/presents/' + present.img + ')';
        }
        return a;
    },
    createIslandDiv: function (island) {
        var offset = this.map2Pixel(island.x, island.y),
            island_type = this.islands[island.type],
            iconoffset = this.map2Pixel(island_type.width, island_type.height),
            left = this.cssOffset.x + offset.x + (iconoffset.x >> 1) + 'px',
            top = this.cssOffset.y + offset.y + (iconoffset.y >> 1) + 'px',
            str = '{"tp":"island","id":' + island.id + ',"ix":' + island.x + ',"iy":' + island.y + ',"res":"' + island.res + '"}';
        var wrapper = document.createElement('div');
        wrapper.id = 'island' + '_' + island.x + '_' + island.y;
        wrapper.className = 'islandinfo islandinfo-' + island.type + ' islandinfo-' + island.res;
        wrapper.style.left = left;
        wrapper.style.top = top;
        wrapper.innerHTML = '<a class="gp_island_link" href="#' + btoa(str) + '"></a><div class="islandinfo_malus"></div>';
        return wrapper;
    },
    updateTowns: function (rect) {
        var x = rect[0],
            y = rect[1],
            width = rect[2],
            height = rect[3],
            towns = this.mapData.getTownsFromPosForCurrentViewport(x, y, width, height),
            d = document,
            fragment = d.createDocumentFragment(),
            town, arr, i = towns.length,
            j, getTownType = WMap.getTownType;
        while (i--) {
            town = towns[i];
            if (!d.getElementById(getTownType(town) + '_' + town.id)) {
                arr = this.createTownDiv(town);
                j = arr.length;
                while (j--) {
                    fragment.appendChild(arr[j]);
                }
            }
        }
        this.elm.towns[0].appendChild(fragment);
    },
    updateWonders: function (rect) {
        var x = rect[0],
            y = rect[1],
            width = rect[2],
            height = rect[3],
            wonders = this.mapData.getWondersFromPosForCurrentViewport(x, y, width, height),
            wonder, ix, iy, d = document,
            fragment = d.createDocumentFragment(),
            i = wonders.length;
        while (i--) {
            wonder = wonders[i];
            if ((ix = wonder.ix) === undefined && (iy = wonder.iy) === undefined) {
                continue;
            }
            if (!d.getElementById('wonder_' + ix + '_' + iy)) {
                fragment.appendChild(this.createWonderDiv(wonder));
            }
        }
        this.elm.wonders.append(fragment);
    },
    updatePresents: function (rect) {
        var x = rect[0],
            y = rect[1],
            width = rect[2],
            height = rect[3],
            presents = this.mapData.getPresentsFromPosForCurrentViewport(x, y, width, height),
            present, ix, iy, d = document,
            fragment = d.createDocumentFragment(),
            i = presents.length,
            presentElement;
        while (i--) {
            present = presents[i];
            if ((ix = present.ix) === undefined && (iy = present.iy) === undefined) {
                continue;
            }
            if ((presentElement = this.createPresentDiv(present))) {
                fragment.appendChild(presentElement);
            }
        }
        this.elm.presents.append(fragment);
    },
    updateIslandInfos: function (rect) {
        var x = rect[0],
            y = rect[1],
            width = rect[2],
            height = rect[3];
        var islandinfos = this.mapData.getIslandInfos(x, y, width, height);
        var d = document;
        var fragment = d.createDocumentFragment();
        var i = islandinfos.length,
            island;
        while (i--) {
            island = islandinfos[i];
            if (!d.getElementById('island_' + island.x + '_' + island.y)) {
                fragment.appendChild(this.createIslandDiv(island));
            }
        }
        this.elm.islands.append(fragment);
    },
    $: function (x, y) {
        var id = 'tile_' + x + "_" + y;
        var tile = document.getElementById(id);
        return tile;
    }
};

var MapBookmarks = {
    bookmarks_menu: null,
    emptyContainer: null,
    wndSaveLocation: null,
    init: function () {
        var that = this;
        this.bookmarks_menu = $("#map_bookmark_link_hover_menu");
        $("#map_position_bookmark_btn").click(function () {
            if (!that.exist()) {
                that.loadMenuTemplate();
            } else {
                that.bookmarks_menu.toggle();
            }
        }).mouseover(function (e) {
            if (!that.exist()) {
                that.loadMenuTemplate();
            } else {
                that.bookmarks_menu.show();
            }
        }).mouseout(function (e) {
            if (that.exist() && !that.containsHtmlNode(e.relatedTarget)) {
                that.bookmarks_menu.hide();
            }
        });
    },
    loadMenuTemplate: function () {
        var that = this,
            tagName, className, pos, href, mnuTools;
        Layout.ajaxGet('map_favorites', 'index', {}, false, function (data) {
            mnuTools = $("#map_tools").append(data.html);
            that.bookmarks_menu = mnuTools.find("#map_bookmark_link_hover_menu");
            that.emptyContainer = that.bookmarks_menu.find("div.no_bookmarks");
            that.bookmarks_menu.click(function (e) {
                e.stopPropagation();
                tagName = (e.target.tagName || "").toLowerCase();
                className = (e.target.className || "").toLowerCase();
                if (tagName == "a") {
                    href = (e.target.getAttribute("href") || "").split("#");
                    href = href[1] ? href[1] : href[0];
                    if (href == "save_location") {
                        if (!that.wndSaveLocation) {
                            that.showSaveLocationWindow();
                        }
                    } else {
                        pos = href.split("_");
                        if (pos[1]) {
                            if (className == "select") {
                                pos = pos[1].split("-");
                                WMap.jumpToPosByCoords(pos[0], pos[1]);
                            } else if (className == "cancel") {
                                that.remove(pos[1]);
                            }
                        }
                    }
                }
            }).mouseout(function (e) {
                if (!that.containsHtmlNode(e.relatedTarget)) {
                    $(this).hide();
                }
            }).mousedown(function (e) {
                e.stopPropagation();
            }).show();
        });
    },
    showSaveLocationWindow: function () {
        var that = this,
            gd = GameData;
        if (!gd.MapFavoritesWndTemplate) {
            Layout.ajaxGet('map_favorites', 'getwndbody', {}, false, function (data) {
                GameData.MapFavoritesWndTemplate = data.html;
                that.createSaveLocationWindow();
            });
        } else {
            that.createSaveLocationWindow();
        }
    },
    createSaveLocationWindow: function () {
        var htmlWindow, gd = GameData,
            that = this;
        this.wndSaveLocation = Layout.dialogWindow.open(gd.MapFavoritesWndTemplate, 'Save location', 235, 190, function () {
            that.wndSaveLocation = null;
        }, true);
        $("#save_location_x").val(WMap.mapX);
        $("#save_location_y").val(WMap.mapY);
    },
    add: function () {
        var that = this,
            title = $("#save_location_title").val(),
            x = parseInt($("#save_location_x").val(), 10),
            y = parseInt($("#save_location_y").val(), 10);
        Layout.ajaxPost('map_favorites', 'add', {
            x: x,
            y: y,
            name: title
        }, false, function (data) {
            if (!data.error) {
                that.bookmarks_menu.find(".bookmarks").append('<li class="bookmark bookmark_' + data.id + '"><a class="select" href="#pos_' + x + '-' + y + '">' + title + '</a><a class="cancel" href="#id_' + data.id + '" href="#"></a></li>');
                that.wndSaveLocation.close();
                that.emptyContainer.hide();
            }
        });
    },
    remove: function (id) {
        var that = this;
        Layout.ajaxPost('map_favorites', 'delete', {
            id: id
        }, false, function (data) {
            that.bookmarks_menu.find("li.bookmark_" + id).remove();
            if (that.isEmpty()) {
                that.emptyContainer.show();
            }
        }, true);
    },
    exist: function () {
        return this.bookmarks_menu.length > 0;
    },
    containsHtmlNode: function (element) {
        return this.bookmarks_menu.find(element).length > 0;
    },
    isEmpty: function () {
        return this.bookmarks_menu.find("li.bookmark").length == 0;
    }
};

var Message = {
    id: null,
    folder_id: null,
    part: null,
    anti_spam_enabled: false,
    post_id: 0,
    elm: {},
    create: function (part, show_captcha) {
        this.elm.captcha = this.elm.captcha || $('#captcha_area');
        this.elm.captcha_bg = this.elm.captcha_bg || $('#captcha_bg');
        if (show_captcha && (Message.anti_spam_enabled || Message.isMassmail())) {
            Message.part = part;
            this.elm.captcha.find('#captcha_image').attr('src', '/captcha.php?id=' + Game.player_id + '&' + (new Date()).getTime());
            this.elm.captcha.appendTo('body').fadeIn();
            this.elm.captcha_bg.appendTo('body').fadeTo(400, 0.5);
            this.elm.captcha.show();
            return false;
        }
        this.elm.captcha.hide();
        this.elm.captcha_bg.hide();
        var params;
        if (part === 'new') {
            params = {
                message: $('#message_new_message').val(),
                recipients: $('#message_recipients').val(),
                subject: $('#message_subject').val()
            };
            if ((this.elm.captchaInput = this.elm.captcha.find('#captcha')).length) {
                params.captcha = this.elm.captchaInput.val();
                this.elm.captchaInput.val('');
            }
            Layout.wnd.getOpenFirst(Layout.wnd.TYPE_MESSAGE).requestContentPost('message', 'create', params);
        } else if (part == 'reply') {
            params = {
                answer: $('#message_reply_message').val(),
                id: Message.id
            };
            Layout.wnd.getOpenFirst(Layout.wnd.TYPE_MESSAGE).requestContentPost('message', 'reply', params);
        }
        return false;
    },
    reloadCaptcha: function () {
        this.elm.captcha.find('#captcha_image').attr('src', '/captcha.php?id=' + Game.player_id + '&' + (new Date()).getTime());
    },
    isMassmail: function () {
        var recipients = $('#message_recipients').val().split(';');
        var recipient_count = 0;
        $.each(recipients, function (id, recipient) {
            if ($.trim(recipient).length > 0) {
                recipient_count++;
            }
        });
        return recipient_count > 1;
    },
    edit: function (part) {
        $('#message_' + part + '_preview').hide();
        var create = $('#message_' + part + '_create').show();
        if (part == 'reply') {
            var post = $('#message_post_container');
        }
        return false;
    },
    reply: function () {
        var create = $('#message_reply_create');
        create.show();
        $('#message_reply_preview').hide();
        $("#message_message_list").addClass("reply_message");
        return false;
    },
    markAll: function (status) {
        $(".message_date INPUT[type='checkbox']").attr('checked', status);
    },
    setRecipients: function (recipients) {
        var _recipients = [];
        $.each(recipients, function (id, recipient) {
            $.merge(_recipients, [recipient.name]);
        });
        $('#message_recipients').val(_recipients.join('; '));
    },
    toggleMenu: function () {
        var folder_menu_messages = $("#folder_menu_messages"),
            folders = folder_menu_messages.find(".hor_scrollbar_cont span.folder"),
            folders_len = folders.length,
            row = 1,
            per_row = Math.ceil(folders_len / 3),
            size = 0,
            sizes = [0, 0, 0],
            col;
        folder_menu_messages.toggle();
        folders.each(function (index, obj) {
            if (index + 1 >= row * per_row && row < 3) {
                sizes[row - 1] = size;
                size = 0;
                row++;
            }
            size += $(this).outerWidth();
            if (index == folders_len - 1) {
                sizes[row - 1] = size;
            }
        });
        $("#folder_menu_messages .hor_scrollbar_cont").width(Math.max(sizes[0], sizes[1], sizes[2]));
        $("#message_list").toggleClass('with_menu');
    },
    editFolder: function (folder_id) {
        Message.folder_id = folder_id;
        var params = {
            folder_id: folder_id
        };
        $('#message_folder #folder_name_' + folder_id).css('display', 'block');
        $('#message_folder #save_folder_name_' + folder_id).css('display', 'block');
        $('##message_folder folder_link_' + folder_id).css('display', 'none');
        Layout.ajaxGet('message', 'getFolder', params, true, function (data) {
            $('#message_folder #folder_name_' + folder_id).val(data.folder.name);
        });
        return false;
    },
    saveFolder: function () {
        var params = {
            folder_id: Message.folder_id,
            name: $('#message_folder #folder_name_' + Message.folder_id).val()
        };
        Layout.wnd.getOpenFirst(Layout.wnd.TYPE_MESSAGE).requestContentPost('message', 'saveFolder', params);
    },
    newFolder: function () {
        var params = {
            folder_id: Message.folder_id,
            name: $('#message_folder #new_folder_name').val()
        };
        Layout.wnd.getOpenFirst(Layout.wnd.TYPE_MESSAGE).requestContentPost('message', 'saveFolder', params);
    },
    delFolder: function (folder_id) {
        var params = {
            folder_id: folder_id
        };
        Layout.wnd.getOpenFirst(Layout.wnd.TYPE_MESSAGE).requestContentPost('message', 'delFolder', params);
    },
    openReportAffrontDialog: function (post_id) {
        this.post_id = post_id;
        $('#message_report_affront_dialog').show();
    },
    closeReportAffrontDialog: function () {
        this.post_id = 0;
        $('#message_report_affront_dialog').hide();
    },
    addBlockedPlayer: function () {
        var input = $('input[name="player_name"]');
        var params = {
            'player_name': input.val()
        };
        Layout.wnd.getOpenFirst(Layout.wnd.TYPE_MESSAGE).requestContentPost('message', 'add_blocked_player', params);
        return false;
    },
    removeBlockedPlayer: function (blocked_player_id) {
        var params = {
            'blocked_player_id': blocked_player_id
        };
        Layout.wnd.getOpenFirst(Layout.wnd.TYPE_MESSAGE).requestContentPost('message', 'remove_blocked_player', params);
        return false;
    },
    deleteAllMessages: function () {
        Layout.showConfirmDialog('Delete all messages', 'Do you want to delete all messages in this folder?', function () {
            Layout.newMessage.getWnd().sendMessage('submitForm', 'message_form', 'delete_all_of_folder');
        });
    }
};

function GPMinimap(type, scale) {
    scale = scale || 0.2;
    var elm = {
        'canvas': $('#' + type + 'map_canvas'),
        'minimap': $('#' + type + 'map'),
        'layer': $('#' + type + 'map_islands_layer')
    },
        doc = document;
    var max_island_width = 1152,
        max_island_height = 640,
        c_w, c_h;
    var size = {
        'x': 0,
        'y': 0
    },
        mapScroll = {
            'x': 0,
            'y': 0
        },
        maxScroll = {
            minx: type === 'mini' ? 200 : 0,
            miny: type === 'mini' ? 50 : 0,
            maxx: ((WMap.map_size * WMap.mapTiles.tileSize.x) / 2) * scale,
            maxy: ((WMap.map_size * WMap.mapTiles.tileSize.y)) * scale
        },
        lastMousePos = {},
        mapPos = {},
        visibleChunks = {},
        dblclick = false;
    var that = this;
    var threshold = 200,
        moveSinceLastLoad = {
            x: 0,
            y: 0
        };

    function bindHandler() {
        if (type === 'pico') {
            $('#picomap_wrapper').mousedown(handlerDown);
        } else {
            elm.canvas.mousedown(handlerDown);
        }
    }

    function expand() {
        var d = Layout.getDocument();
        elm.canvas.removeClass('collapsed').addClass('expanded');
        elm.minimap.show();
        c_h = elm.canvas.height();
        c_w = elm.canvas.width();
        maxScroll.maxx -= c_w;
        maxScroll.maxy -= c_h;
        var e = GPEvents.global.map.mapjump + '.' + type;
        d.unbind(e).bind(e, function () {
            that.refresh();
        });
        if (type == 'mini') {
            d.unbind('keydown.GPMinimap').bind('keydown.GPMinimap', function (e) {
                if (e.keyCode === 27) {
                    collapse();
                }
            });
        }
    }

    function collapse() {
        var d = Layout.getDocument();
        maxScroll.maxx += c_w;
        maxScroll.maxy += c_h;
        elm.canvas.removeClass('expanded').addClass('collapsed');
        elm.minimap.hide();
        elm.layer.empty();
        d.unbind('keydown.GPMinimap').unbind(GPEvents.global.map.mapjump + '.' + type);
    }

    function getRect() {
        size = MapTiles.pixel2Map(c_w / scale, c_h / scale);
        var rect = {
            x: mapPos.x,
            y: mapPos.y,
            w: size.x,
            h: size.y
        };
        return rect;
    }
    this.fillCanvas = function () {
        mapPos.x = WMap.mapX;
        mapPos.y = WMap.mapY;
        expand();
        var p1 = WMap.mapTiles.map2Pixel(mapPos.x, mapPos.y);
        var p2 = {
            x: -((WMap.scroll.x || p1.x) + (WMap.mapTiles.xSize)) * scale,
            y: -((WMap.scroll.y || p1.y) + (WMap.mapTiles.ySize)) * scale
        };
        that.scrollMapTo2({
            x: p2.x,
            y: p2.y
        });
        loadChunks(getRect());
        this.drawBorders();
    };

    function loadChunks(rect) {
        var chunks = WMap.mapData.getCoveredChunks(rect.x, rect.y, rect.w, rect.h);
        WMap.mapData.checkReload(rect.x, rect.y, rect.w, rect.h, function () {
            var i = chunks.length;
            while (i--) {
                renderChunk(WMap.mapData.getChunk(chunks[i].x, chunks[i].y));
            }
        });
        return chunks;
    }

    function sortTowns(chunk) {
        var i, t;
        var towns = {};
        for (i in chunk.towns) {
            t = chunk.towns[i];
            if (!towns[t.x + '_' + t.y]) {
                towns[t.x + '_' + t.y] = [];
            }
            towns[t.x + '_' + t.y].push(t);
        }
        return towns;
    }

    function renderChunk(chunk) {
        if (!chunk) {
            return false;
        }
        var islands = chunk.islands,
            towns = sortTowns(chunk),
            visibleIslands = {},
            fragment;
        var do_once = false;
        var frag_x = 0,
            frag_y = 0;
        var id, pos, x, y, island;
        var clip_left, clip_right, clip_top, clip_bottom;
        id = type + '_chunk_' + chunk.chunk.x + '_' + chunk.chunk.y;
        if ((fragment = doc.getElementById(id))) {
            frag_x = parseInt(fragment.style.left, 10);
            frag_y = parseInt(fragment.style.top, 10);
        } else {
            fragment = doc.createElement('div');
            fragment.className = 'm_chunk';
            fragment.id = id;
            do_once = true;
        }
        var iId;
        var i = islands.length;
        while (i--) {
            pos = MapTiles.map2Pixel(islands[i].x, islands[i].y);
            x = ~~ (pos.x * scale);
            y = ~~ (pos.y * scale);
            if (do_once) {
                fragment.style.left = (frag_x = x) + 'px';
                fragment.style.top = (frag_y = y) + 'px';
                do_once = false;
            }
            iId = type + '_i' + islands[i].x + '_' + islands[i].y;
            if (clip()) {
                if ((island = doc.getElementById(iId))) {
                    island.parentNode.removeChild(island);
                }
            } else {
                if (!(island = doc.getElementById(iId))) {
                    island = doc.createElement('div');
                    island.style.left = x - frag_x + 'px';
                    island.style.top = y - frag_y + 'px';
                    island.style.backgroundImage = 'url(http://cdn.grepolis.com/images/game/map/' + Math.round(1 / scale) + '/' + MapTiles.islands[islands[i].type].img + ')';
                    island.className = 'm_island';
                    island.id = iId;
                    fragment.appendChild(island);
                    var key = islands[i].x + '_' + islands[i].y;
                    var t_arr;
                    if ((t_arr = towns[key])) {
                        var a = t_arr.length,
                            t_id;
                        while (a--) {
                            t_id = (type + '_t' + towns[key][a].id).replace('=', '');
                            if (towns[key][a].fc && !doc.getElementById(t_id)) {
                                island.appendChild(renderTown(towns[key][a]));
                            }
                        }
                    }
                }
                visibleIslands[islands[i].x + '_' + islands[i].y] = true;
            }
        }

        function renderTown(t) {
            var town = doc.createElement('div');
            town.id = t_id;
            town.style.left = ~~ (t.ox * scale) + 'px';
            town.style.top = ~~ (t.oy * scale) + 'px';
            town.style.color = "#" + (t.fc || 'f00');
            t.type && (town.style['font-size'] = '50%');
            town.innerHTML = ((t.type && t.type === 'inv_spo') || t.invitation_spot) ? '\u25b2' : '\u25cf';
            town.className = 'm_town';
            return town;
        }

        function clip() {
            clip_left = mapScroll.x + ~~ (x + max_island_width * scale) < 0;
            clip_right = mapScroll.x + x > c_w;
            clip_top = mapScroll.y + ~~ (y + max_island_height * scale) < 0;
            clip_bottom = mapScroll.y + y > c_h;
            return (clip_left || clip_right || clip_top || clip_bottom);
        }
        visibleChunks[chunk.chunk.x + '_' + chunk.chunk.y] = {
            'fragment': fragment
        };
        if (!doc.getElementById(fragment.id)) {
            elm.layer[0].appendChild(fragment);
        }
    }
    this.drawBorders = function () {
        var fragment = doc.createDocumentFragment(),
            map_size = WMap.mapTiles.mapSize,
            coords, pixels, sea_id, div, border_length = map_size / 10,
            psize = scale * WMap.mapTiles.map2Pixel(border_length).x,
            pos = getRect();
        var x_start = bound(pos.x, 0, map_size),
            y_start = bound(pos.y, 0, map_size),
            x_count = Math.ceil(pos.w / border_length),
            y_count = Math.ceil(pos.h / border_length),
            i, j;
        x_start -= x_start % border_length;
        y_start -= y_start % border_length;
        for (i = 0; i <= x_count; i++) {
            for (j = 0; j <= y_count; j++) {
                coords = [x_start + (border_length * i), y_start + (border_length * j)];
                pixels = WMap.mapTiles.map2Pixel.apply(WMap.mapTiles, coords);
                sea_id = WMap.getSea(coords[0], coords[1]).join('');
                if (!doc.getElementById(type + '_sea_' + sea_id)) {
                    div = doc.createElement('div');
                    div.style.width = psize + 'px';
                    div.style.height = psize + 'px';
                    div.className = 'm_border';
                    div.id = type + '_sea_' + sea_id;
                    div.style.left = pixels.x * scale + 'px';
                    div.style.top = pixels.y * scale + 'px';
                    div.innerHTML = sea_id;
                    fragment.appendChild(div);
                }
            }
        }
        elm.layer[0].appendChild(fragment);
    };
    this.refresh = function () {
        collapse();
        elm.layer.empty();
        this.fillCanvas();
    };
    this.scrollMapTo = function (pos) {
        pos.x = bound(~~pos.x, -maxScroll.maxx, maxScroll.minx);
        pos.y = bound(~~pos.y, -maxScroll.maxy, maxScroll.miny);
        elm.minimap.css({
            left: mapScroll.x = pos.x,
            top: mapScroll.y = pos.y
        });
        var pos2 = WMap.mapTiles.pixel2Map(-mapScroll.x / scale, -mapScroll.y / scale);
        mapPos.x = pos2.x;
        mapPos.y = pos2.y;
    };
    this.scrollMapTo2 = function (pos) {
        var diff_x = pos.x - mapScroll.x,
            diff_y = pos.y - mapScroll.y;
        if (Math.abs(moveSinceLastLoad.x += diff_x) > threshold || Math.abs(moveSinceLastLoad.y += diff_y) > threshold) {
            doMagic();
        }
        this.scrollMapTo({
            x: pos.x += (c_w >> (type === 'pico' ? 0 : 1)),
            y: pos.y += (c_h >> (type === 'pico' ? 0 : 1))
        });
    };

    function handlerDown(e) {
        var obj = elm.canvas[0];
        if (e.target.parentNode.id === 'map_tools') {
            return;
        }
        if (obj.setCapture && obj.detachEvent) {
            obj.setCapture();
            obj.attachEvent('onmousemove', handlerMove);
            obj.attachEvent('onmouseup', handlerUp);
            obj.attachEvent('onlosecapture', handlerUp);
        } else {
            doc.addEventListener('mousemove', handlerMove, true);
            doc.addEventListener('mouseup', handlerUp, true);
        }
        lastMousePos = {
            'x': e.clientX,
            'y': e.clientY
        };
        moveSinceLastLoad = {
            'x': e.clientX,
            'y': e.clientY
        };
        handlerMove(e);
        e.preventDefault();
    }

    function handlerUp(e) {
        var target = e.target || e.srcElement;
        if (dblclick || (target && target.id === 'map_zoom')) {
            window.clearTimeout(dblclick);
            dblclick = false;
            handleDblClick(e);
        } else {
            dblclick = window.setTimeout(function () {
                doMagic();
                dblclick = false;
            }, 350);
        }
        var obj = elm.canvas[0];
        if (obj.releaseCapture && obj.detachEvent) {
            obj.detachEvent('onlosecapture', handlerUp);
            obj.detachEvent('onmousemove', handlerMove);
            if (!dblclick) {
                obj.detachEvent('onmouseup', handlerUp);
            }
            obj.releaseCapture();
        } else {
            doc.removeEventListener('mouseup', handlerUp, true);
            doc.removeEventListener('mousemove', handlerMove, true);
        }
    }

    function handleDblClick(e) {
        dblclick = false;
        switch (type) {
        case 'pico':
            Minimap.fillCanvas();
            break;
        case 'mini':
            zoomIn(e);
            break;
        }
    }

    function zoomIn(e) {
        e = jQuery.event.fix(e);
        var pos = findPos(e),
            id = 'minimap_viewport',
            box = doc.getElementById(id) || doc.createElement('div'),
            w = c_w * scale,
            h = c_h * scale,
            y_off = $('#main_area').offset().top;
        box.id = id;
        box.style.left = e.pageX - (w >> 1) + 'px';
        box.style.top = e.pageY - y_off - (h >> 1) + 'px';
        box.style.width = w + 'px';
        box.style.height = h + 'px';
        elm.canvas.append(box);
        $(box).animate({
            'width': c_w,
            'height': c_h,
            'left': 0,
            'top': 0
        }, 400, 'swing', function () {
            WMap.jumpToPosByCoords(pos.x, pos.y);
            collapse();
            box.parentNode.removeChild(box);
        });
    }

    function findPos(e) {
        var off = $('#main_area').offset();
        var m_x = -mapScroll.x + (e.pageX - off.left),
            m_y = -mapScroll.y + (e.pageY - off.top);
        return WMap.mapTiles.pixel2Map(m_x / scale + (max_island_width >> 1) - WMap.size.x / 2, m_y / scale + (max_island_height >> 1) - WMap.size.y / 2);
    }

    function cleanUp(chunks) {
        var chunk, i = chunks.length,
            vChunk;
        while (i--) {
            if ((chunk = visibleChunks[chunks[i].x + '_' + chunks[i].y])) {
                chunk.keep = true;
            }
        }
        for (i in visibleChunks) {
            if ((vChunk = visibleChunks[i]).keep) {
                vChunk.keep = false;
            } else {
                try {
                    elm.layer[0].removeChild(vChunk.fragment);
                } catch (e) {} finally {
                    delete vChunk.fragment;
                    vChunk = 0;
                    delete visibleChunks[i];
                }
            }
        }
        elm.layer.find('div.m_border').remove();
    }
    this.getScale = function () {
        return scale;
    };

    function handlerMove(e) {
        var diff_x = e.clientX - lastMousePos.x,
            diff_y = e.clientY - lastMousePos.y;
        that.scrollMapTo({
            x: mapScroll.x + diff_x,
            y: mapScroll.y + diff_y
        });
        lastMousePos.x = e.clientX;
        lastMousePos.y = e.clientY;
        if (Math.abs(moveSinceLastLoad.x += diff_x) > threshold || Math.abs(moveSinceLastLoad.y += diff_y) > threshold) {
            doMagic();
        }
    }

    function doMagic() {
        cleanUp(loadChunks(getRect()));
        that.drawBorders();
        moveSinceLastLoad.x = 0;
        moveSinceLastLoad.y = 0;
    }
    bindHandler();
    this.count = function () {
        return 'element count: ' + elm.layer.find('div.m_chunk, div.m_island, div.m_town').length;
    };
    this.benchmark = function () {
        $('#minimap_islands_layer').empty();
        console.profile();
        Minimap.fillCanvas();
        console.profileEnd();
    };
}

var RecruitUnits = {
    units: [],
    current_town_id: null,
    current_unit_id: null,
    slider: null,
    help: false,
    popup: $('<div id="overview_help" class="small"><div class="top"></div><div class="middle"></div><div class="bottom"></div></div>'),
    c_width: 735,
    c_height: 370,
    recruit_units: null,
    recruit_tabs: null,
    showAllUnits: false,
    res: {},
    old_value: [],
    max_build: [],
    towns: {},
    special: null,
    regular: null,
    regular_visible: null,
    init: function () {
        addIsNavalToGameDataUnits();
        RecruitUnits.special = $('.special');
        RecruitUnits.regular = $('.regular');
        RecruitUnits.container = $('.game_inner_box.recruit_overview');
        RecruitUnits.regular_visible = true;
        $('.confirm').mousePopup(new MousePopup('Train units'));
        $('.reload').mousePopup(new MousePopup('Reset'));
        $('.toggle_troops_in_town.own').mousePopup(new MousePopup('Show only own troops'));
        $('.toggle_troops_in_town.all').mousePopup(new MousePopup('Show all troops'));
        $('.all_troops_of_town').mousePopup(new MousePopup('All units from this city'));
        $('.recruit_overview_toggle').mousePopup(new MousePopup('Show mythical units.'));
        $('.recruit_overview_toggle').click(function () {
            if ($('.recruit_overview_toggle').hasClass('game_arrow_right')) {
                $('.recruit_overview_toggle').mousePopup(new MousePopup('Show mythical units.'));
            } else {
                $('.recruit_overview_toggle').mousePopup(new MousePopup('Show land and sea units'));
            }
        });
        $('.queues_list').each(function () {
            var table_class = $(this).prev().attr('class');
            $(this).addClass(table_class);
        });
        $('.recruit_overview .max_build').bind('click', function () {
            if (!$(this).next().hasClass('inactive')) {
                $(this).parent().find('input').val(parseInt($(this).html().match(/\d+/), 10));
            }
        });
        $('.toggle_troops_in_town.own').click(function () {
            if ($(this).hasClass('disabled')) {
                return false;
            }
            $(this).addClass('disabled');
            $('.toggle_troops_in_town.all').removeClass('disabled');
            $('.all_troops_of_town').removeClass('disabled');
            RecruitUnits.toggleTownUnits();
        });
        $('.toggle_troops_in_town.all').click(function () {
            if ($(this).hasClass('disabled')) {
                return false;
            }
            $(this).addClass('disabled');
            $('.toggle_troops_in_town.own').removeClass('disabled');
            $('.all_troops_of_town').removeClass('disabled');
            RecruitUnits.toggleTownUnits();
        });
        $('.all_troops_of_town').click(function () {
            if ($(this).hasClass('disabled')) {
                return false;
            }
            $(this).addClass('disabled');
            $('.toggle_troops_in_town.all').removeClass('disabled');
            $('.toggle_troops_in_town.own').removeClass('disabled');
            RecruitUnits.toggleAllTroopsOfTowns();
        });
        $.each(GameData.units, function (unit) {
            $("#overview_unit_" + unit).setPopup(unit);
        });
        $('#recruit_tabs').bind('click', function (event) {
            RecruitUnits.select(event.target || event.srcElement);
            event.stopPropagation();
        });
        $('#unit_overview_town_list').bind('click', function (event) {
            RecruitUnits.selectTown(event);
            event.stopPropagation();
        });
        RecruitUnits.recruit_units = $('#recruit_units').detach();
        RecruitUnits.recruit_units.bind('click', function (event) {
            event.stopPropagation();
        });
        RecruitUnits.initSlider();
        $('tr.place_command').each(function () {
            if ($(this).find('.casted_power_call_of_the_ocean').length != 0) {
                $(this).find('.power_icon#call_of_the_ocean').addClass('disabled');
            }
            if ($(this).find('.casted_power_fertility_improvement').length != 0) {
                $(this).find('.power_icon#fertility_improvement').addClass('disabled');
            }
        });
        RecruitUnits.reInitializeOrders();
    },
    queueUnits: function (elem) {
        if (!$(elem).hasClass('inactive')) {
            var classes = $(elem).parent().attr('class').split(' ', 2);
            var town_id = classes[0].match(/\d+/);
            var unit_type = classes[1].replace(/unit_/g, '');
            $(elem).parent().find('input').val(parseInt($(elem).parent().find('.max_build').html().match(/\d+/), 10));
            RecruitUnits.updateResAndQueue(town_id, unit_type, GameData.units[unit_type].resources, GameData.units[unit_type].population);
        }
    },
    selectTown: function (ev) {
        var ele = ev.originalTarget || ev.srcElement;
        if (ele) {
            var preselect = null;
            if (ele.tagName == 'A') {
                return;
            }
            while (ele.parentNode != null && (ele.parentNode.tagName != 'UL' || ele.parentNode.id.indexOf('town_units_overview') != -1)) {
                if (ele.className.indexOf('place_unit') != -1) {
                    preselect = ele.className.replace('place_unit unit_', '');
                }
                ele = ele.parentNode;
            }
            RecruitUnits.current_town_id = ~~ (ele.id.replace(/\D+/g, ''));
            var self = $(ele);
        } else {
            ele = GodsOverview.getParentTownElement($(ev.target));
            RecruitUnits.current_town_id = ~~ (ele.attr('id').replace(/\D+/g, ''));
            self = ele;
        }

        function openCloseTown() {
            var queues = self.find('#units_' + RecruitUnits.current_town_id + ' .queue');
            if (!self.hasClass('selected')) {
                var p = self.position();
                self.attr('style', 'position:absolute;left:' + p.left + 'px;top:' + p.top + 'px;z-index:5;');
                self.animate({
                    left: 0,
                    top: self.parent().parent().scrollTop(),
                    width: RecruitUnits.c_width,
                    height: RecruitUnits.c_height
                }, 500, function () {
                    self.find('#units_' + RecruitUnits.current_town_id).append(RecruitUnits.recruit_units);
                    RecruitUnits.showAvailableUnits();
                });
                self.addClass('selected');
                queues.each(function () {
                    $(this).parent().show();
                });
                queues.first().parents('.queues').addClass('active').prev().hide();
            } else {
                self.removeAttr('style').removeClass('selected');
                RecruitUnits.recruit_units.detach();
                queues.each(function () {
                    ele = $(this);
                    if (ele.children().length == 0) {
                        ele.parent().hide();
                    }
                });
                queues.first().parents('.queues').removeClass('active').prev().show();
            }
        }
        if (RecruitUnits.units[RecruitUnits.current_town_id]) {
            openCloseTown();
        } else {
            Layout.ajaxGet('town_overviews', 'town_units', {
                town_id: RecruitUnits.current_town_id
            }, false, function (response) {
                RecruitUnits.units[RecruitUnits.current_town_id] = {
                    units: response
                };
                openCloseTown();
            });
        }
    },
    calculateFavor: function (town_id, current_favor, power, favor_for_power) {
        $('tr#ov_town_' + town_id).find('.power_icon#' + power).addClass('disabled');
        if (current_favor - favor_for_power < favor_for_power) {
            $('.power_icon#' + power).each(function () {
                $(this).addClass('disabled');
            });
        }
    },
    orderSelectedUnit: function () {
        var order_count = ~~ ($('#recruit_amount').val());
        var naval = GameData.units[RecruitUnits.current_unit_id].transport === null;
        if (order_count == 0) {
            return;
        }
        var unit_image = '<div id="recruit_order_%1" class="place_unit ordered_unit_' + this.current_unit_id + '" style="background:url(http://cdn.grepolis.com/images/game/units/' + this.current_unit_id + '_25x25.png);"><span class="place_unit_black small bold">' + order_count + '</span><span class="place_unit_white small bold">' + order_count + '</span>' + '<span class="finished_at" style="display:none;">%2</span>' + '<span class="order_unit_type" style="display:none;">%3</span></div>';
        var town_id = this.current_town_id;
        var ajax_data = {};
        ajax_data.units = {};
        ajax_data.units[this.current_unit_id] = order_count;
        ajax_data.town_id = town_id;
        ajax_data.no_bar = town_id != Game.townId ? 1 : 0;
        var queue = $('#units_' + this.current_town_id + ' .queues').find('.naval , .ground');
        Layout.ajaxPost('town_overviews', 'recruit_units', ajax_data, false, function (return_data) {
            var type_order_id_map = {},
                order_id;
            for (order_id in return_data.unit_types) {
                if (return_data.unit_types.hasOwnProperty(order_id)) {
                    type_order_id_map[return_data.unit_types[order_id]] = order_id;
                }
            }
            $.each(ajax_data.units, function (type, amount) {
                RecruitUnits.units[town_id].units[type].max_build -= parseInt(amount, 10);
                var recruit_order_id = type_order_id_map[type];
                var finished_at = return_data.order_finished_times[recruit_order_id];
                unit_image = unit_image.replace("%1", recruit_order_id);
                unit_image = unit_image.replace("%2", finished_at);
                unit_image = unit_image.replace("%3", type);
                queue[naval ? 1 : 0].getElementsByTagName('div')[1].innerHTML += unit_image;
                var res_span = $('#units_' + town_id + ' .unit_town_resources .resource_count .count');
                var i = 0;
                var unit_res = jQuery.extend(true, {}, GameData.units[type].resources, {
                    population: GameData.units[type].population
                }),
                    res, recruit_units = function (index, town) {
                        if (town.id == town_id) {
                            town_index = index;
                        }
                    };
                for (res in unit_res) {
                    var ele = res_span[i++];
                    RecruitUnits.units[town_id][res] = ele.innerHTML = ~~ (ele.innerHTML - unit_res[res] * amount * RecruitUnits.getResearchModificationFactor(town_id, type));
                    var town_index = 0;
                    $.each(RecruitUnits.towns, recruit_units);
                    RecruitUnits.towns[town_index].resources[res] = parseInt(ele.innerHTML, 10);
                    if (unit_res[res] > 0) {
                        $(ele).removeClass("town_storage_full");
                    }
                }
            });
            var counter = 0;
            $.each(return_data.order_ids, function (key, id) {
                $('#order_' + return_data.order_ids[id]).unbind();
                if (counter == 0) {
                    $('#order_tmp').attr('id', 'order_' + return_data.order_ids[id]);
                    $('#order_' + return_data.order_ids[id]).addClass('orderer_unit_' + return_data.unit_types[id]);
                }
                $('#order_' + return_data.order_ids[id]).mousePopup(new MousePopup('<div id="ordered_unit_popup">' + GameData.units[return_data.unit_types[id]].name + '<br /><img src="http://cdn.grepolis.com/images/game/res/time.png" alt=""/><span class="eta"></span></div>' + '<script type="text/javascript">$("#ordered_unit_popup .eta").countdown(' + return_data.order_finished_times[id] + ')<\/script>'));
                counter++;
            });
            var units = RecruitUnits.units[town_id].units,
                unit;
            for (unit in units) {
                if (units[unit].max_build > 0) {
                    var resources = jQuery.extend(true, {}, GameData.units[unit].resources, {
                        population: GameData.units[unit].population
                    }),
                        res;
                    for (res in resources) {
                        var new_max = parseInt(RecruitUnits.units[town_id][res] / (resources[res]), 10);
                        new_max = Math.max(new_max, 0);
                        if (new_max < units[unit].max_build) {
                            units[unit].max_build = new_max;
                        }
                    }
                }
            }
            RecruitUnits.showAvailableUnits();
            RecruitUnits.reInitializeOrders();
        });
    },
    getResearchModificationFactor: function (town_id, unit_type) {
        return (GameData.units[unit_type].is_naval && $('#ov_town_' + town_id).hasClass('mathematics_researched')) || (!GameData.units[unit_type].is_naval && $('#ov_town_' + town_id).hasClass('conscription_researched')) ? 0.9 : 1;
    },
    orderSelectedUnitInOverview: function (town_id) {
        var order_count = 0;
        var unit_image = {};
        var order_is_empty = true;
        var ajax_data = {};
        ajax_data.units = {};
        $.each(GameData.units, function (unit) {
            order_count = $('#town_' + town_id + '_count_' + unit).val();
            if (order_count == '' || typeof order_count == 'undefined') {
                order_count = 0;
            }
            if (order_count > 0) {
                order_is_empty = false;
                ajax_data.units[unit] = parseInt(order_count, 10);
                unit_image[unit] = '<div id="recruit_order_%1" class="place_unit ordered_unit_' + unit + '" style="background:url(http://cdn.grepolis.com/images/game/units/' + unit + '_25x25.png);"><span class="place_unit_black small bold">' + order_count + '</span><span class="place_unit_white small bold">' + order_count + '</span>' + '<span class="finished_at" style="display:none;">%2</span>' + '<span class="order_unit_type" style="display:none;">%3</span></div>';
            }
        });
        ajax_data.town_id = town_id;
        ajax_data.no_bar = town_id != Game.townId ? 1 : 0;
        var queue = $('#ov_town_' + town_id).next('tr').find('.naval , .ground');
        if (!order_is_empty) {
            Layout.ajaxPost('town_overviews', 'recruit_units', ajax_data, false, function (return_data) {
                var type_order_id_map = {};
                var max_order_id = 0,
                    order_id;
                for (order_id in return_data.unit_types) {
                    if (return_data.unit_types.hasOwnProperty(order_id)) {
                        max_order_id = Math.max(order_id, max_order_id);
                        type_order_id_map[return_data.unit_types[order_id]] = max_order_id;
                    }
                }
                $.each(ajax_data.units, function (type, amount) {
                    var res_span = $('div.recruit_overview #town_' + town_id + '_res span.count');
                    var i = 0;
                    var unit_res = jQuery.extend(true, {}, GameData.units[type].resources, {
                        population: GameData.units[type].population
                    }),
                        res;
                    for (res in unit_res) {
                        var ele = res_span[i++];
                        ele.innerHTML = ~~ (ele.innerHTML - unit_res[res] * amount * RecruitUnits.getResearchModificationFactor(town_id, type));
                        var town_index = 0,
                            recruit_units = function (index, town) {
                                if (town.id == town_id) {
                                    town_index = index;
                                }
                            };
                        $.each(RecruitUnits.towns, recruit_units);
                        RecruitUnits.towns[town_index].resources[res] = parseInt(ele.innerHTML, 10);
                        if (unit_res[res] > 0) {
                            $(ele).removeClass("town_storage_full");
                        }
                    }
                    if (amount != 0) {
                        var recruit_order_id = type_order_id_map[type];
                        var finished_at = return_data.order_finished_times[recruit_order_id];
                        var naval = GameData.units[type].transport === null;
                        unit_image[type] = unit_image[type].replace("%1", recruit_order_id);
                        unit_image[type] = unit_image[type].replace("%2", finished_at);
                        unit_image[type] = unit_image[type].replace("%3", type);
                        queue[naval ? 1 : 0].innerHTML += unit_image[type];
                    }
                });
                RecruitUnits.getMaxBuildOfUnits(town_id);
                RecruitUnits.resetRecruitInTown(town_id);
                RecruitUnits.reInitializeOrders();
            });
        }
    },
    reInitializeOrders: function () {
        $('div.units_order_overview span.finished_at').each(function (index, elem) {
            var finished_at = parseInt($(elem).html(), 10);
            if (!isNumber(finished_at)) {
                return;
            }
            var recruit_order_id = parseInt($(elem).parent().attr('id').substr(14), 10);
            if (RecruitUnits.setupOrderTimer(finished_at, recruit_order_id) == false) {
                $(elem).parent().remove();
                return;
            }
            $('#recruit_order_' + recruit_order_id).mousePopup(new MousePopup(s('Completion %1', formatDateTimeNice(finished_at, false))));
        });
    },
    setupOrderTimer: function (finished_at, recruit_order_id) {
        var delay = (finished_at - Timestamp.now()) * 1000;
        delay = Math.max(delay, 0);
        if (delay == 0) {
            return false;
        }
        $(document).stopTime('recruit_order_' + recruit_order_id);
        $(document).oneTime(delay, 'recruit_order_' + recruit_order_id, function () {
            $('#recruit_order_' + recruit_order_id).remove();
        });
        return true;
    },
    getMaxBuildOfUnits: function (town_id) {
        var wood = parseInt($('.recruit_overview #town_' + town_id + '_res .wood span.count').html(), 10);
        var stone = parseInt($('.recruit_overview #town_' + town_id + '_res .stone span.count').html(), 10);
        var iron = parseInt($('.recruit_overview #town_' + town_id + '_res .iron span.count').html(), 10);
        var town_population = parseInt($('.recruit_overview #town_' + town_id + '_res .town_population span.count').html(), 10);
        var max_build;
        $.each(GameData.units, function (unit) {
            if (GameData.units[unit].id != 'militia') {
                var max_build_for_wood = GameData.units[unit].resources.wood != 0 ? Math.floor(wood / (GameData.units[unit].resources.wood * RecruitUnits.getResearchModificationFactor(town_id, unit))) : wood;
                var max_build_for_stone = GameData.units[unit].resources.stone != 0 ? Math.floor(stone / (GameData.units[unit].resources.stone * RecruitUnits.getResearchModificationFactor(town_id, unit))) : stone;
                var max_build_for_iron = GameData.units[unit].resources.iron != 0 ? Math.floor(iron / (GameData.units[unit].resources.iron * RecruitUnits.getResearchModificationFactor(town_id, unit))) : iron;
                var max_build_for_favor = GameData.units[unit].favor != 0 ? Math.floor(Layout.favor[GameData.units[unit].god] / GameData.units[unit].favor) : -1;
                var max_build_for_population = GameData.units[unit].population != 0 ? Math.floor(town_population / GameData.units[unit].population) : town_population;
                if (GameData.units[unit].favor != 0) {
                    max_build = Math.min(Math.min(Math.min(max_build_for_wood, max_build_for_stone), Math.min(max_build_for_iron, max_build_for_population)), max_build_for_favor);
                } else {
                    max_build = Math.min(Math.min(max_build_for_wood, max_build_for_stone), Math.min(max_build_for_iron, max_build_for_population));
                }
                RecruitUnits.max_build[GameData.units[unit].id] = parseInt(max_build);
                if (!$('.recruit_overview tr#ov_town_' + town_id + ' .max_build.unit_' + GameData.units[unit].id).parent().hasClass('inactive')) {
                    $('.recruit_overview tr#ov_town_' + town_id + ' .max_build.unit_' + GameData.units[unit].id).html((parseInt(max_build) ? '+' + parseInt(max_build) : ''));
                }
                if (!parseInt(max_build)) {
                    $('.recruit_overview tr#ov_town_' + town_id + '.max_build.unit_' + GameData.units[unit].id).remove();
                    $('.recruit_overview tr#ov_town_' + town_id + ' input#town_' + town_id + '_count_' + GameData.units[unit].id).remove();
                }
            }
        });
    },
    select: function (ele) {
        if (typeof ele != 'string') {
            while (ele.parentNode.className.indexOf('recruit_tab') != -1 || ele.parentNode.id != 'recruit_tabs') {
                ele = ele.parentNode;
            }
        } else {
            ele = $('#town_units_overview').find('#' + ele).parent()[0];
        }
        var unit_id = RecruitUnits.current_unit_id = ele.getElementsByTagName('div')[0].id;
        RecruitUnits.recruit_tabs.each(function (element) {
            this.parentNode.className = this.parentNode.className.replace('selected', '');
        });
        RecruitUnits.slider.setMax(RecruitUnits.units[RecruitUnits.current_town_id].units[unit_id].max_build)
        RecruitUnits.slider.setValue(RecruitUnits.units[RecruitUnits.current_town_id].units[unit_id].max_build)
        var img = RecruitUnits.recruit_units.find('img')[0];
        img.src = Game.img() + '/game/units/' + unit_id + '_90x90.jpg'
        img.alt = GameData.units[unit_id].name;
        document.getElementById('unit_order_unit_name').innerHTML = GameData.units[unit_id].name;
        RecruitUnits.showCosts({
            unit_id: unit_id,
            count: RecruitUnits.slider.getValue()
        })
        RecruitUnits.showCosts({
            count: 1
        })
        $(ele).addClass('selected');
    },
    showCosts: function (options) {
        RecruitUnits.unit_id = options.unit_id || RecruitUnits.current_unit_id;
        RecruitUnits.count = options.count || 1;
        var all = RecruitUnits.count > 1 ? 'all' : 'unit'
        var unit = RecruitUnits.units[RecruitUnits.current_town_id].units[RecruitUnits.unit_id];
        for (var res_id in unit.resources) {
            document.getElementById('unit_order_' + all + '_' + res_id).innerHTML = unit.resources[res_id] * RecruitUnits.count;
        }
        document.getElementById('unit_order_' + all + '_favor').innerHTML = unit.favor * RecruitUnits.count;
        document.getElementById('unit_order_' + all + '_pop').innerHTML = unit.population * RecruitUnits.count;
        document.getElementById('unit_order_' + all + '_build_time').innerHTML = readableSeconds(unit.build_time * RecruitUnits.count);
    },
    resetUnit: function (ele, unit_type) {
        delete RecruitUnits.units[RecruitUnits.current_town_id][unit_type];
        $(ele).parent().remove();
    },
    initSlider: function () {
        var elements = RecruitUnits.recruit_units.children('#recruit_box').children().children();
        RecruitUnits.slider = new Slider({
            elementMin: $(elements[1]),
            elementMax: $(elements[2]),
            elementDown: $(elements[3]),
            elementUp: $(elements[5]),
            elementInput: $(elements[6]),
            elementSlider: $(elements[4])
        });
        RecruitUnits.slider._elementSlider.bind('change', function (event, ui) {
            RecruitUnits.showCosts({
                count: RecruitUnits.slider.getValue()
            });
        });
    },
    showAvailableUnits: function () {
        RecruitUnits.recruit_units.hide();
        RecruitUnits.recruit_units.slideDown();
        var type;
        if (!RecruitUnits.recruit_tabs) {
            RecruitUnits.recruit_tabs = $('#recruit_tabs .recruit_unit');
        }
        RecruitUnits.recruit_tabs.each(function () {
            type = this.id;
            var tab = $(this);
            var unittype = RecruitUnits.units[RecruitUnits.current_town_id].units[type];
            if (unittype && unittype.max_build > 0) {
                tab.parent().show();
                tab.children()[0].innerHTML = tab.children()[1].innerHTML = unittype.count;
                tab.next()[0].innerHTML = unittype.max_build;
            } else {
                tab.parent().hide();
            }
        });
        RecruitUnits.select('sword');
    },
    toggleTownUnits: function () {
        if (!RecruitUnits.showAllUnits) {
            Layout.ajaxGet('town_overviews', 'all_units', {}, false, function (return_data) {
                RecruitUnits.showAllUnits = !RecruitUnits.showAllUnits;
                $.each(return_data.all_units, function (town_id, units) {
                    var list = $('#units_' + town_id + ' .current_units').html('');
                    $.each(units, function (type, sum) {
                        $('tr#ov_town_' + town_id + ' span.count_' + type).html(sum);
                        if (sum > 0 && type != 'town_id') {
                            list.append('<div style="background-image: url(http://cdn.grepolis.com/images/game/units/' + type + '_25x25.png);" class="place_unit unit_' + type + '"><span class="place_unit_black bold small">' + sum + '</span><span class="place_unit_white bold small">' + sum + '</span></div>');
                        }
                    });
                    $('#toggle_unit_link .middle').text('Show only own troops');
                });
            });
        } else {
            Layout.ajaxGet('town_overviews', 'own_units', {}, false, function (return_data) {
                RecruitUnits.showAllUnits = !RecruitUnits.showAllUnits;
                $.each(return_data.own_units, function (town_id, units) {
                    var list = $('#units_' + town_id + ' .current_units').html('');
                    $.each(units, function (type, sum) {
                        $('tr#ov_town_' + town_id + ' span.count_' + type).html(sum);
                        if (sum > 0 && type != 'town_id') {
                            list.append('<div style="background-image: url(http://cdn.grepolis.com/images/game/units/' + type + '_25x25.png);" class="place_unit unit_' + type + '"><span class="place_unit_black bold small">' + sum + '</span><span class="place_unit_white bold small">' + sum + '</span></div>');
                        }
                    });
                    $('#toggle_unit_link .middle').text('Show all troops');
                });
            });
        }
    },
    toggleAllTroopsOfTowns: function () {
        $('tr.place_command').each(function () {
            var that = this;
            $.each(GameData.units, function (type, sum) {
                $(that).find('span.count_' + type).html($(that).find('span.total_count_' + type).html());
            });
        });
    },
    updateResAndQueue: function (town_id, unit_id, unit_resources, unit_population) {
        var input_value = $('input#town_' + town_id + '_count_' + unit_id).val();
        var wood = input_value * unit_resources.wood;
        var iron = input_value * unit_resources.iron;
        var stone = input_value * unit_resources.stone;
        var town_population = input_value * unit_population;
        var costs = {
            'wood': wood,
            'stone': stone,
            'iron': iron,
            'town_population': parseInt(town_population)
        };
        if (typeof RecruitUnits.old_value[town_id] == "undefined") {
            RecruitUnits.old_value[town_id] = [];
        }
        if (typeof RecruitUnits.old_value[town_id][unit_id] == "undefined") {
            RecruitUnits.old_value[town_id][unit_id] = 0;
        }
        $.each(costs, function (key, value) {
            var current_res_diff = $('#town_' + town_id + '_res .' + key + ' .diff').html();
            if (typeof unit_resources[key] == "undefined") {
                var resource_type = parseInt(unit_population);
            } else {
                var resource_type = parseInt(unit_resources[key]);
            }
            if (current_res_diff) {
                if (parseInt(input_value)) {
                    var new_res_diff = (parseInt(current_res_diff) + ((parseInt(value) / parseInt(input_value)) * parseInt(RecruitUnits.old_value[town_id][unit_id])) - parseInt(value));
                } else {
                    var new_res_diff = (parseInt(current_res_diff) + (resource_type * parseInt(RecruitUnits.old_value[town_id][unit_id])));
                }
            } else {
                var new_res_diff = (0 + ((parseInt(value) / parseInt(input_value)) * parseInt(RecruitUnits.old_value[town_id][unit_id])) - parseInt(value));
            }
            $('#town_' + town_id + '_res .' + key + ' .diff').html(new_res_diff);
            $('#town_' + town_id + '_res .' + key + ' .diff').css({
                display: 'block'
            });
            if (parseInt(new_res_diff) < -(parseInt($('#town_' + town_id + '_res .' + key + ' span.count').html()))) {
                $('#town_' + town_id + '_res .' + key + ' .diff').css('color', '#c00');
            } else {
                $('#town_' + town_id + '_res .' + key + ' .diff').css('color', '#0a0');
            }
        });
        RecruitUnits.old_value[town_id][unit_id] = input_value;
    },
    resetRecruitInTown: function (town_id) {
        $('#ov_town_' + town_id + ' input').each(function () {
            $(this).val('');
        });
        var costs = {
            'wood': 0,
            'stone': 0,
            'iron': 0,
            'town_population': 0
        };
        $.each(costs, function (key, value) {
            $('#town_' + town_id + '_res .' + key + ' .diff').html(value);
            $('#town_' + town_id + '_res .' + key + ' .diff').css({
                display: 'none'
            });
        });
        RecruitUnits.old_value = [];
    },
    toggleVisibleUnits: function (ele) {
        if (!RecruitUnits.regular || !RecruitUnits.special) {
            RecruitUnits.special = $('.special');
            RecruitUnits.regular = $('.regular');
        }
        if (RecruitUnits.regular_visible) {
            ele.className = 'game_arrow_left recruit_overview_toggle';
            RecruitUnits.showMythologicalUnits();
        } else {
            ele.className = 'game_arrow_right recruit_overview_toggle';
            RecruitUnits.showRegularUnits();
        }
        RecruitUnits.regular_visible = !RecruitUnits.regular_visible;
    },
    showRegularUnits: function () {
        RecruitUnits.regular.show();
        RecruitUnits.special.hide();
        RecruitUnits.container.css('width', '760px');
    },
    showMythologicalUnits: function () {
        RecruitUnits.special.show();
        RecruitUnits.regular.hide();
        RecruitUnits.container.css('width', '600px');
    }
};
var CultureOverview = {
    running_celebration_data: {},
    celebration_duration_data: {},
    init: function (_running_celebration_data, _celebration_duration_data) {
        this.running_celebration_data = is_array(_running_celebration_data) ? {} : _running_celebration_data;
        this.celebration_duration_data = _celebration_duration_data;
        CultureOverview.updateCelebrationProgress();
    },
    addCelebrationToData: function (town_id, type, finished_at) {
        if (!this.running_celebration_data.hasOwnProperty(town_id)) {
            this.running_celebration_data[town_id] = {};
        }
        var town_celeb_data = this.running_celebration_data[town_id];
        town_celeb_data[type] = finished_at;
    },
    startCelebration: function (celebration_type, town_id) {
        var button = $('#ov_town_' + town_id + ' .type_' + celebration_type);
        if (button.hasClass('disabled')) {
            return false;
        }
        var data = {};
        data.town_id = town_id;
        data.celebration_type = celebration_type;
        data.no_bar = town_id != Game.townId ? 1 : 0;
        Layout.ajaxPost('town_overviews', 'start_celebration', data, false, function (return_data) {
            if (celebration_type == 'games') {
                Kontagent.trackEvent({
                    userId: Game.uid,
                    eventName: 'celebration',
                    subtype1: Game.market_id,
                    subtype2: Game.world_id,
                    subtype3: 'buy_pa_feature'
                }, function () {});
            }
            button.toggleClass('disabled');
            var town_id = return_data.town_id;
            $.each(return_data.startable_celebrations, function (type, data) {
                if (!data) {
                    var tempbutton = $('#ov_town_' + town_id + ' .type_' + type);
                    if (!tempbutton.hasClass('disabled')) {
                        tempbutton.addClass('disabled');
                    }
                }
            });
            CultureOverview.addCelebrationToData(town_id, return_data.celebration_type, return_data.finished_at);
            var item = $('#town_' + town_id + '_timer_' + return_data.celebration_type + '.celebration_progressbar');
            item.html($('<div></div><span class="eta">' + return_data.finished_at + '</span>'));
            var eta = item.children('.eta');
            eta.countdown(eta.html(), {});
            var points_wrapper = $('#place_battle_points');
            points_wrapper.find('div.points_count').html(return_data.player_kills + '/' + return_data.needed_kills_for_next);
            points_wrapper.find('div.points_bar').width(return_data.player_kills > return_data.needed_kills_for_next ? '150px' : (return_data.player_kills * 150 / return_data.needed_kills_for_next) + 'px');
        }.bind(button), {}, 'start_celebration');
        return false;
    },
    updateCelebrationProgress: function () {
        for (var town_id in CultureOverview.running_celebration_data) {
            if (!CultureOverview.running_celebration_data.hasOwnProperty(town_id)) {
                continue;
            }
            var town_celeb_data = CultureOverview.running_celebration_data[town_id];
            for (var celeb_type in town_celeb_data) {
                if (!town_celeb_data.hasOwnProperty(celeb_type)) {
                    continue;
                }
                var progress = (100 - ((town_celeb_data[celeb_type] - Timestamp.now()) / CultureOverview.celebration_duration_data[celeb_type]));
                if (progress >= 100) {
                    progress = 100;
                    delete CultureOverview.running_celebration_data[town_id];
                    $('#town_' + town_id + '_timer_' + celeb_type + '.celebration_progressbar').html('');
                } else {
                    var item = $('#town_' + town_id + '_timer_' + celeb_type + '.celebration_progressbar').children().first('div');
                    item.css('width', progress + '%');
                }
            }
        }
        if ($('#culture_overview_wrapper').length) {
            window.setTimeout(CultureOverview.updateCelebrationProgress, 5000);
        }
    }
};
var BuildingOverview = {
    buidling_data: null,
    ele: null,
    col: null,
    special: null,
    regular: null,
    res: {},
    init: function (_building_data) {
        var list = $('table#building_overview tr td.building');
        this.special = $('.special');
        this.regular = $('.regular');
        this.building_data = _building_data;
        list.bind('mouseover', function (ev) {
            BuildingOverview.ele = $('#' + ev.currentTarget.className.replace(' ', '_'));
            var current_target = ev.currentTarget.className;
            current_target = current_target.substr(0, current_target.indexOf(' '));
            BuildingOverview.col = $('table#building_overview tr td.' + current_target)
            BuildingOverview.highlightColumn(ev)
            $(ev.currentTarget).bind('mouseout', function () {
                BuildingOverview.ele.removeClass('selected');
                $(this).unbind('mouseout')
            });
        });
    },
    highlightColumn: function (ev) {
        var t_id = ev.currentTarget.parentNode.id.replace('ov_town_', '');
        var b_id = ev.currentTarget.className;
        b_id = b_id.substr(0, b_id.indexOf(' '));
        this.res.needed = this.building_data[t_id][b_id].resources_for;
        this.res.needed.town_population = this.building_data[t_id][b_id].population_for;
        var t_info = document.getElementById('town_' + t_id + '_res');
        var res_info = t_info.getElementsByTagName('div');
        var res_info_jQElem = [];
        for (var i = 0; i < 4; i++) {
            res_info_jQElem.push($(res_info[i]));
        }
        this.res.current = {
            wood: ~~ (res_info_jQElem[0].find('.count').html()),
            stone: ~~ (res_info_jQElem[1].find('.count').html()),
            iron: ~~ (res_info_jQElem[2].find('.count').html()),
            town_population: ~~ (res_info_jQElem[3].find('.count').html())
        };
        var obj_diff;
        i = 0;
        for (var key in this.res.needed) {
            obj_diff = res_info_jQElem[i].find('.diff');
            if (this.res.current[key] < this.res.needed[key]) {
                obj_diff.addClass('notenough');
            } else {
                obj_diff.removeClass('notenough');
            }
            obj_diff.html('-' + this.res.needed[key]);
            i++;
        }
        this.ele.addClass('selected');
    },
    updateResAndLevel: function (t_id, object) {
        var t_info = $('#town_' + t_id + '_res');
        var res_info = t_info.find('.count');
        var newres = {};
        var i = 0;
        for (var key in this.res.current) {
            if (t_id != Game.townId) {
                newres[key] = (this.res.current[key] - this.res.needed[key]);
                ITowns.getTown(t_id).resources(newres);
            }
            $(res_info[i]).html((this.res.current[key] - this.res.needed[key]));
            if (this.res.needed[key] > 0) {
                $(res_info[i]).removeClass('town_storage_full');
            }
            i++;
        }
        var lvl = ~~ (object.parentNode.getElementsByTagName('a')[1].innerHTML.replace(/\W/g, ''));
        object.parentNode.getElementsByTagName('a')[1].innerHTML = lvl + 1;
        $(t_info).animate({
            color: '#0a0'
        }, 250, function () {
            $(this).animate({
                color: '#000'
            })
        })
    },
    build: function (building_id, town_id, tear_down, object) {
        var data = {};
        data.building_id = building_id;
        data.town_id = town_id;
        data.tear_down = tear_down ? 1 : 0;
        data.no_bar = town_id != Game.townId ? 1 : 0;
        if ($(object).hasClass('disabled')) {
            return false;
        }
        Layout.ajaxPost('town_overviews', 'build_building', data, false, function (data, flag) {
            if (!tear_down) {
                BuildingOverview.updateResAndLevel(town_id, object);
            }
        });
        return false;
    },
    toggleSpecialBuildings: function (ele) {
        if (this.special == null && this.regular == null) {
            this.special = $('.special');
            this.regular = $('.regular');
        }
        if (this.special.is(':hidden')) {
            ele.className = 'game_arrow_left recruit_overview_toggle';
            this.special.show();
            this.regular.css({
                display: 'none'
            });
            $('.game_inner_box.recruit_overview').css('width', '600px');
        } else {
            ele.className = 'game_arrow_right recruit_overview_toggle';
            this.regular.show();
            this.special.css({
                display: 'none'
            });
            $('.game_inner_box.recruit_overview').css('width', '760px');
        }
    }
};
var Sort = {
    sort_by: null,
    sortBy: function (string) {
        this.sort_by = string;
    },
    sortedBy: function (string) {
        return this.sort_by == string;
    },
    qsort: function (array) {
        var greater = new Array(),
            less = new Array();
        if (array == null || array.length <= 1) {
            return array;
        } else {
            var index = Math.floor(Math.random() * (array.length - 1));
            var pivot = array[index];
            array.splice(index, 1);
            for (var i = 0; i < array.length; i++) {
                var obj = array[i];
                var x = $(obj).find('span.sortable.' + this.sort_by).text();
                var y = $(pivot).find('span.sortable.' + this.sort_by).text();
                if (x.isLTE(y)) {
                    less.push(obj);
                } else {
                    greater.push(obj);
                }
            }
            return (this.qsort(less).concat(pivot)).concat(this.qsort(greater));
        }
    }
};
var HidesOverview = {
    help: false,
    popup: $('<div id="overview_help" class="small"><div class="top"></div><div class="middle"></div><div class="bottom"></div></div>'),
    init: function () {
        $('#buttons .cancel').mousePopup(new MousePopup('reset'));
        $('#buttons .confirm').mousePopup(new MousePopup('Store silver coins'));
        $('.help').mousePopup(new MousePopup('Show help'));
    },
    clearSelection: function (town_id) {
        $('#town_hide_' + town_id).val(0);
    },
    storeIronInTown: function (town_id) {
        var iron_to_store = $('#town_hide_' + town_id).val();
        var value = $('#ov_town_' + town_id + ' ' + 'span.eta').html();
        var current_iron_stored = value.substr(1, (value.indexOf('/') - 1));
        var max_storage = value.substr(value.indexOf('/') + 1, (value.lastIndexOf(')') - 1 - value.indexOf('/')));
        if (iron_to_store == 0 || '') {
            HumanMessage.error('You must store at least 1 silver coin.');
            return;
        } else {
            Layout.ajaxPost('town_overviews', 'store_iron', {
                'town_id': town_id,
                'active_town_id': Game.townId,
                'iron_to_store': iron_to_store
            }, false, function (data) {
                ITowns.setResources({
                    'iron': data.iron
                }, town_id);
                var elem = $('#town_' + town_id + '_res .iron .count');
                elem.removeClass('town_storage_full');
                elem.html(data.iron);
                $('#ov_town_' + town_id + ' ' + 'span.eta').html('(' + (data.iron_stored + parseInt(current_iron_stored, 10)) + '/' + max_storage + ')');
                $('#ov_town_' + town_id + ' ' + 'div#bar').css('width', ((data.iron_stored + parseInt(current_iron_stored, 10)) / parseInt(max_storage, 10) * 100) + '%');
                HidesOverview.clearSelection(town_id);
            });
        }
    },
    showHelp: function (town_id, window, step) {
        if (typeof (step) == 'undefined') {
            if (HidesOverview.help) {
                HidesOverview.hideHelp();
                return;
            } else {
                step = 0;
            }
        }
        var wnd = window.getJQElement().find('.gpwindow_content');
        HidesOverview.popup.appendTo(wnd);
        switch (step) {
        case 0:
            HidesOverview.help = true;
            HidesOverview.clearSelection();
            var anchor = $('#town_hide_' + town_id);
            if (anchor.length == 0) {
                var anchor = $('#no_hide_' + town_id);
                HidesOverview.popup.children('.middle').text('You still have to build a cave in this city.');
            } else {
                HidesOverview.popup.children('.middle').text('Enter the number of silver coins and confirm the entry with the green arrow.');
            }
            HidesOverview.popup.addClass('top_align').css({
                top: (anchor.offset().top - wnd.offset().top + 15) + 'px',
                left: (anchor.offset().left - wnd.offset().left) + 'px'
            }).show();
            break;
        default:
            HidesOverview.hideHelp();
            break;
        }
    },
    hideHelp: function () {
        HidesOverview.help = false;
        HidesOverview.popup.hide().remove();
    }
};
var Overviews = {
    power_popup_data: [],
    initializePowerPopupForTownOverview: function (power_id, town_id, finished_at) {
        var str = PopupFactory.texts[power_id];
        str = '<script type="text/javascript">' + '$("div.temple_power_popup_info .eta").countdown(' + finished_at + ',{})' + '</script>' + str.slice(0, str.length - 16) + '<br />' + '<img alt="" src="http://cdn.grepolis.com/images/game/res/time.png">' + '<span class="eta">' + finished_at + '</span>' + '</p></div></div>';
        $("#cp_town_" + town_id + "_" + power_id).mousePopup(new MousePopup(str));
    },
    openOverview: function (overview_type, controller) {
        controller = controller || 'town_overviews';
        var w = Layout.wnd.getOpenFirst(Layout.wnd.TYPE_TOWN_OVERVIEWS);
        if (!w) {
            Layout.wnd.Create(Layout.wnd.TYPE_TOWN_OVERVIEWS, 'Overviews', {
                overview_type: overview_type,
                controller: controller
            }, Game.townId);
        } else {
            w.requestContentGet(controller, overview_type, {
                'town_id': Game.townId
            });
        }
    },
    openBuildingWnd: function (town_id, building) {
        Layout.townSwitch(town_id);
        Layout.buildingWindow.open(building);
    }
};
var TownsOverview = {
    wnd_handle: null,
    toggle_mode: 0,
    options: null,
    init: function (wnd_handle, options) {
        TownsOverview.wnd_handle = wnd_handle;
        TownsOverview.options = options;
        PopupFactory.addTexts({
            header_town: 'City (points)',
            header_island: 'Island coordinates (ocean)',
            header_wood: 'Amount of wood',
            header_stone: 'Amount of rock',
            header_iron: 'Amount of silver',
            header_free_pop: 'Free population',
            header_storage: 'Warehouse capacity',
            header_research_points: 'Research points',
            header_main: 'Senate activity',
            header_barracks: 'Barracks activity',
            header_docks: 'Harbor activity',
            header_trade: 'Available trade capacity',
            header_culture: 'City festivals',
            header_outbound_attacks: 'Attacks led by you',
            header_inbound_attacks: 'Attacks on your city!',
            header_casts: 'Active spells'
        });
        $('.tag_header').each(function () {
            $(this).setPopup($(this).attr('id'));
        });
        $.each(GameData.units, function (unit) {
            $(".col.header." + unit).each(function () {
                $(this).setPopup(unit);
            });
        });
        $("li[id^='ov_town_']").hover(function () {
            $(this).addClass('datahighlight');
        }, function () {
            $(this).removeClass('datahighlight');
        });
        $("#units_show").click(function () {
            TownsOverview.toggle();
        });
        var handleESCallbackEvent = function (args, data, isScrollDown) {
                var view = $(data.view);
                $.each(view, function (key, elem) {
                    if (elem.id !== undefined) {
                        var town_id = elem.id.replace("ov_town_", "");
                        $.each($('#town_' + town_id + '_casts').children(), function (jkey, jelem) {
                            var power_id = jelem.id.replace(town_id + "_", "");
                            $('#' + town_id + '_' + power_id).setPopup(power_id);
                        })
                    }
                });
                TownsOverview.toggle(true);
            };
        var es_data = {
            elem_id: 'table_scroll_content',
            controller: 'town_overviews',
            action: 'towns_overview',
            limit: options.es_limit,
            last_element: options.es_elem_total,
            first_element: options.es_elem_start,
            window_handle: wnd_handle,
            es_pagination_id: 'es_page_town_ov',
            callback: handleESCallbackEvent
        };
        TownsOverviewEndlessScroll = new GPEndlessScroll(es_data);
    },
    toggle: function (reposition) {
        if (!reposition) {
            TownsOverview.toggle_mode++;
            TownsOverview.toggle_mode = TownsOverview.toggle_mode % TownsOverview.options.toggle_count;
        }
        var x_offset = -175 * TownsOverview.toggle_mode;
        $('#header_units_div').animate({
            left: x_offset + 'px'
        }, 'slow');
        $("div[id^='units_div_']").each(function () {
            $(this).animate({
                left: x_offset + 'px'
            }, 'slow');
        });
    }
};
var OuterUnitsOverview = {
    init: function () {
        GPEvents.global.bind(GPEvents.global.command.send_back_support, function (event, data) {
            $('#outer_troops_units_' + data.support_id).remove();
        });
    }
};

var GodsOverview = {
    current_town_id: 0,
    templates: null,
    data: null,
    town_gods: null,
    favor: null,
    prev_target: null,
    prev_castable_powers: null,
    init: function (templates, data) {
        var html = tmpl(templates.tmpl, {
            towns: data.towns,
            mystic_units: data.mythological_unit_types,
            gods: data.data_gods
        }),
            html_bottom = tmpl(templates.tmpl_bottom, {
                god_favor_overview: data.god_favor_overview,
                god_favor: data.god_favor,
                max_favor: data.max_favor
            }),
            that = this;
        this.templates = templates;
        this.data = data;
        this.town_gods = data.town_gods;
        this.favor = data.god_favor_overview;
        $("#gods_overview_wrapper").append(html);
        $("#gods_overview_bottom").append(html_bottom);
        $('#gods_overview_towns').bind('click', function (e) {
            e.stopPropagation();
            that.selectTown(e);
        });
    },
    getParentTownElement: function (el) {
        var wanted_class = 'town_item',
            that = this;
        if (!el.hasClass(wanted_class) && el.parent() != undefined) {
            return that.getParentTownElement(el.parent());
        }
        if (el.hasClass(wanted_class)) {
            return el;
        }
        return false;
    },
    selectTown: function (e) {
        var parent = $("#gods_overview_towns"),
            that = this;
        if (!e.target || e.target.tagName == 'A' || parent.find(e.target).length == 0) {
            return;
        }
        var target = this.getParentTownElement($(e.target)),
            data = this.data,
            towns = data.towns,
            town_id = target.attr("id").replace(/\D+/g, ''),
            currTown = towns[town_id],
            castable_powers = $('#town_center_' + town_id + ' .gods_overview_castable_powers');
        this.current_town_id = town_id;
        if (!target.hasClass("selected")) {
            this.updateCastablePowers(castable_powers, town_id, function () {
                target.addClass('selected');
                that.prev_target = target;
            });
        } else {
            target.removeClass('selected');
            castable_powers.hide();
        }
    },
    updateCastablePowers: function (castable_powers, town_id, callback) {
        var data = this.data,
            templates = this.templates;
        if (this.prev_castable_powers) {
            this.prev_castable_powers.empty();
            this.prev_target.removeAttr('style').removeClass('selected');
        }
        this.prev_castable_powers = castable_powers;
        castable_powers.empty().html(tmpl(templates.tmpl_powers, {
            available_powers_for_town: data.available_powers_for_town,
            god_favor_overview: data.god_favor_overview,
            data_gods: data.data_gods,
            available_gods: data.available_gods,
            town_id: town_id,
            towns: data.towns
        })).show();
        if (typeof callback == "function") {
            callback();
        }
    },
    askForChange: function (data) {
        var title = 'Worship %s'.replace('%s', data.new_god_name);
        var text = '<div style="display:block;"><p>' + 'The change to %s has the following effects:'.replace('%s', data.new_god_name) + '</p>' + '<ul class="temple_list" style="list-style:none;">' + (GodsOverview.town_gods[data.town_id] != null ? '<li>' + 'You will lose all mythical units of this city.' + '</li>' : '') + '<li>' + 'Supporting troops with mythological units are leaving the city.' + '</li>' + (GodsOverview.town_gods[data.town_id] != null ? '<li>' + 'You lose %d favor with %s.'.replace('%s', GodsOverview.favor[GodsOverview.town_gods[data.town_id]].god).replace('%d', GodsOverview.favor[GodsOverview.town_gods[data.town_id]].current) + '</li>' : '') + '</ul>' + '<a class="button" style="color: #ffcc66;cursor:pointer;" onclick="GodsOverview.changeGod(' + data.town_id + ',\'' + data.new_god_id + '\', \'' + GodsOverview.town_gods[data.town_id] + '\', w(this))" style="cursor:pointer;"><span class="left"><span class="right"><span class="middle">' + 'Change' + '</span></span></span>' + '<span style="clear:both;"></span></a></div>';
        Layout.showHint(title, text, 400, 250);
    },
    changeGod: function (town_id, new_god_id, old_god_id, wndhandler) {
        var that = this;
        Layout.ajaxPost('town_overviews', 'change_god', {
            'town_id': town_id,
            'god_id': new_god_id
        }, false, function (data) {
            Kontagent.trackEvent({
                userId: Game.uid,
                eventName: 'change_god_to_' + new_god_id,
                subtype1: Game.market_id,
                subtype2: Game.world_id,
                subtype3: 'change_god'
            }, function () {});
            GodsOverview.town_gods[town_id] = new_god_id;
            $('#player_hint_area').remove();
            $('#ov_town_' + town_id + ' .current_myth_units.town_inner_field').html('No myth. units available');
            $('#ov_town_' + town_id).find('.god_micro').attr('class', 'god_micro town_god_' + new_god_id);
            if (typeof GodsOverview.favor[old_god_id] != "undefined") {
                GodsOverview.favor[old_god_id].current = 0;
            }
            that.data.available_powers_for_town = data.available_powers;
            that.updateCastablePowers($('#town_center_' + town_id + ' .gods_overview_castable_powers'), town_id);
            that.updateFavorBar(data.bar.favors);
            wndhandler.close();
        });
    },
    updateTownsCastedPowers: function (town_id, power, finished_at) {
        $('#casted_powers_town_' + town_id).append('<span class="casted_power_' + power + '" id="cp_town_' + town_id + '_' + power + '"><img src="http://cdn.grepolis.com/images/game/towninfo/powers/' + power + '_24x24.png" height="12" width="12" /></span>');
        Overviews.initializePowerPopupForTownOverview(power, town_id, finished_at);
    },
    updateFavorBar: function (favor) {
        var htmlGod;
        $.each(favor, function (index, value) {
            htmlGod = $("#god_" + index);
            if (value.production > 0) {
                htmlGod.removeClass("favor_no_god").addClass("favor");
            } else {
                htmlGod.removeClass("favor").addClass("favor_no_god");
            }
            htmlGod.find("span").each(function (index) {
                if (index == 1) {
                    if (value.current > 0) {
                        $(this).removeClass("god_favor_icon_disabled").addClass("god_favor_icon");
                    } else {
                        $(this).removeClass("god_favor_icon").addClass("god_favor_icon_disabled");
                    }
                } else if (index == 2) {
                    if (value.current > 0) {
                        $(this).removeClass("god_favor_text_disabled").addClass("god_favor_text");
                    } else {
                        $(this).removeClass("god_favor_text").addClass("god_favor_text_disabled");
                    }
                    $(this).text(value.current);
                }
            });
        });
    }
};
var CommandsOverview = {
    nodes: {},
    filter_type: "#tab_all",
    town_filter: "",
    timer: null,
    init: function (templates, data) {
        var that = this;
        this.templates = templates;
        this.data = data;
        this.redrawOverview(this.town_filter, $("#command_type_select").val());
        $("#game_list_footer").empty().append(tmpl(templates.footer, {
            filter_types: data.filter_types,
            filter_type: that.filter_type
        }));
        var command_class, filter, command;
        $('div.support_filter').click(function () {
            filter = $(this);
            filter.toggleClass('inactive');
            command_class = filter.attr('class').split(' ');
            $('img.' + command_class[1]).each(function () {
                command = $(this).parent().parent().parent();
                if (command.css('display') == 'none') {
                    command.fadeIn();
                } else {
                    command.fadeOut();
                }
            });
        });
        $("#command_type_select").change(function (e) {
            var select = that.nodes.command_filter ? that.nodes.command_filter : $("#command_filter"),
                type = $(this).val();
            that.filter_type = type;
            select[type !== "#tab_all" ? "hide" : "show"]();
            that.redrawOverview(that.town_filter, type);
        });
        $('#filter_towns').bind({
            'keyup': that.keyUpHandler,
            'keydown': that.keyDownHandler
        }).one('focus', {}, function () {
            this.value = '';
        });
    },
    keyDownHandler: function (e) {
        window.clearTimeout(this.timer);
        this.timer = window.setTimeout(function () {
            CommandsOverview.town_filter = e.target.value;
            CommandsOverview.redrawOverview(CommandsOverview.town_filter, CommandsOverview.filter_type);
        }, 250);
    },
    keyUpHandler: function (e) {},
    initiateCountdowns: function (commands) {
        var cid, command, timestamp = Timestamp.now(),
            html_command, onFinish = function () {
                $(this).parent().parent().fadeOut();
            };
        for (cid in commands) {
            if (commands.hasOwnProperty(cid)) {
                command = commands[cid];
                if (command.arrival_at > timestamp) {
                    html_command = $('span.eta-command-' + command.id);
                    $('#command_' + command.id + ' span.troops_arrive_at').html('(' + 'Arrival' + ' ' + formatDateTimeNice(command.arrival_at, true) + ')');
                    html_command.countdown();
                    html_command.bind('finish', onFinish);
                }
                if (command.command_type && command.command_type == 'revolt') {
                    html_command = $('span.eta-command-revolt-end-' + command.id);
                    if (command.started_at < timestamp) {
                        $('#command_' + command.id + ' span.troops_arrive_at').html('(' + 'End' + ' ' + formatDateTimeNice(command.finished_at, true) + ')');
                        html_command.countdown(command.finished_at, {});
                    } else {
                        $('#command_' + command.id + ' span.troops_arrive_at').html('(' + 'A revolt is being started' + ' ' + formatDateTimeNice(command.started_at, true) + ')');
                        html_command.countdown(command.started_at, {});
                    }
                    html_command.bind('finish', onFinish);
                }
            }
        }
    },
    redrawOverview: function (town_filter, filter_type) {
        var data = this.data,
            templates = this.templates,
            html_tabs, commands = [],
            town_filtered;
        town_filter = town_filter.toLowerCase();
        filter_type = filter_type ? filter_type : this.filter_type;
        town_filtered = data.commands.filter(function (cmd) {
            return town_filter !== "" ? ((cmd.origin_town_name || cmd.destination_town_name) ? (cmd.origin_town_name || cmd.destination_town_name).toLowerCase().match(town_filter) : false) : true;
        });
        switch (filter_type) {
        case "#tab_attacks":
            var incoming_attacks = town_filtered.filter(function (cmd) {
                return ((!cmd.own_command && !cmd.payed_iron && cmd.type !== "support") || !cmd.type);
            });
            var outgoing_attacks = town_filtered.filter(function (cmd) {
                return cmd.own_command && !cmd.payed_iron && ((cmd.type !== "support" && cmd.type !== "abort") || !cmd.type);
            });
            var canceled_orders = town_filtered.filter(function (cmd) {
                return cmd.own_command && cmd.type == "abort";
            });
            commands = commands.concat([{
                html: '<li><h4>' + 'Incoming attacks' + '</h4></li>'
            }], incoming_attacks.length > 0 ? incoming_attacks : [{
                html: '<li class="even"><span class="italic">' + 'There are currently no incoming attacks' + '</span></li>'
            }], [{
                html: '<li><h4>' + 'Outgoing attacks' + '</h4></li>'
            }], outgoing_attacks.length > 0 ? outgoing_attacks : [{
                html: '<li class="even"><span class="italic">' + 'There are currently no outgoing attacks' + '</span></li>'
            }], [{
                html: '<li><h4>' + 'Canceled commands' + '</h4></li>'
            }], canceled_orders.length > 0 ? canceled_orders : [{
                html: '<li class="even"><span class="italic">' + 'There are no current canceled commands' + '</span></li>'
            }]);
            break;
        case "#tab_support":
            var incomming_support = town_filtered.filter(function (cmd) {
                return !cmd.own_command && cmd.type == "support";
            });
            var outgoing_support = town_filtered.filter(function (cmd) {
                return cmd.own_command && cmd.type == "support";
            });
            commands = commands.concat([{
                html: '<li><h4>' + 'Arriving support' + '</h4></li>'
            }], incomming_support.length > 0 ? incomming_support : [{
                html: '<li class="even"><span class="italic">' + 'Currently no incoming support is on it\'s way' + '</span></li>'
            }], [{
                html: '<li><h4>' + 'Outgoing support' + '</h4></li>'
            }], outgoing_support.length > 0 ? outgoing_support : [{
                html: '<li class="even"><span class="italic">' + 'Currently no outgoing support is on it\'s way' + '</span></li>'
            }]);
            break;
        case "#tab_espionage":
            var espionage = town_filtered.filter(function (cmd) {
                return cmd.payed_iron;
            });
            commands = espionage.length > 0 ? espionage : [{
                html: '<li class="even"><span class="italic">' + 'None of your spies are currently traveling' + '</span></li>'
            }];
            break;
        default:
            commands = town_filtered;
            break;
        }
        html_tabs = tmpl(templates.tabs, {
            commands: commands,
            unit_types: data.unit_types,
            player_id: data.player_id,
            attack_types: data.attack_types,
            filter_types: data.filter_types,
            filter_type: filter_type
        });
        $("#command_overview_tabs").empty().append(html_tabs);
        this.initiateCountdowns(commands);
    },
    cancelCommand: function (command_id, command_type) {
        if (command_type == 'command') {
            Layout.ajaxPost('town_overviews', 'cancel_command', {
                id: command_id
            }, false, function () {
                $('#command_' + command_id + ' .cancel').fadeOut('slow');
            });
        } else if (command_type == 'espionage') {
            Layout.ajaxPost('command_info', 'cancel_espionage', {
                id: command_id.replace(/\D*/, '')
            }, false, function () {
                $('#command_' + command_id).fadeOut('slow');
            });
        }
    },
    createFarmLink: function (farm_id, farm_name) {
        return '<a class="gp_town_link" href="#' + (btoa('{"id" : ' + farm_id + ', "ix" : 13, "iy" : 4, "tp" : "farm_town"}')) + '">' + farm_name + '</a> (' + 'Farming village' + ')';
    }
};
var farmTownOverview = {
    openFarmTownOverviewWindow: function () {
        Layout.wnd.Create(Layout.wnd.TYPE_FARM_TOWN_OVERVIEWS, 'Farming villages', {});
    }
};

if (!Overviews) {
    Overviews = {};
}
Overviews.TradeOverview = function TradeOverview(towns, town_tmpl, movements, mov_tmpl) {
    var filteredTownList, filteredMovList, timer, sidebar_visible = false,
        that = this,
        selected = {
            from: null,
            to: null
        },
        resSliders = {
            wood: new UnitSlider(),
            iron: new UnitSlider(),
            stone: new UnitSlider()
        };

    function onCountdownFinish(e) {
        var target = $(e.target),
            id = parseInt(target.parents('div.trade_movement').attr('id').match(/\d+/), 10),
            movement, town, i;
        i = movements.length;
        while (i--) {
            movement = movements[i];
            if (movement.id === id) {
                movements.remove(i);
                break;
            }
        }
        i = towns.length;
        while (i--) {
            town = towns[i];
            if (town.id === movement.from.id) {
                town.out = --town.out || 0;
            } else if (town.id === movement.to.id) {
                town['in'] = --town['in'] || 0;
            }
        }
        that.refreshView();
    }

    function init() {
        var town, itown, i = towns.reverse().length,
            itowns = ITowns;
        while (i--) {
            town = towns[i];
            itown = itowns.getTown(town.id);
            itown.production(town.prod).resources(town.res).storage(town.storage).population(town.pop).points(town.points).capacity(town.cap).fragment(town.frag);
        }
        that.refreshView();
        GPEvents.global.bind(GPEvents.global.town.resources_update + '.trade_overview', function () {
            that.refreshView(true);
        });
        $('#filter_towns').bind({
            'keyup': that.keyUpHandler,
            'keydown': that.keyDownHandler
        }).one('focus', {}, function () {
            this.value = '';
        });
        $('#sort_towns').change(that.sortHandler);
        $('#trade_selected, #trade_control').click(that.clickControlHandler);
        $('#trade_overview_movements_wrapper').bind('finish', onCountdownFinish);
        $('#trade_overview_towns li').draggable2({
            distance: 20,
            helper: function () {
                return $(this).clone().css({
                    width: $(this).width()
                });
            }
        });
        $('#trade_selected div.trade_town_wrapper').droppable2({
            'drop': dropHandler
        });
        for (i in resSliders) {
            resSliders[i].initialize('trade_overview_type_' + i);
        }
    }

    function dropHandler(params) {
        var helper = params.helper.children().first().append('<a href="#pin" class="pin">'),
            key = this[0].id.match(/from|to/).toString(),
            oldid = (this[0].childNodes && this[0].childNodes.length) ? this[0].childNodes[0].id : null;
        params.draggable.addClass('selected');
        $(this).empty().append(helper).addClass('active');
        if (oldid) {
            $('#' + oldid).parent().removeClass('selected');
        }
        selected[key] = helper;
        getDuration();
        that.reInitializeSliders();
    }
    this.destroy = function () {
        GPEvents.global.unbind(GPEvents.global.town.resources_update + '.trade_overview');
    };
    this.clickControlHandler = function (e) {
        var target = e.target,
            href;
        if (!(href = target.href)) {
            return;
        }
        href = href.split(/#/).reverse()[0];
        switch (href) {
        case 'confirm':
            that.trade();
            break;
        case 'cancel':
            clearSelection();
            break;
        case 'swap':
            swap();
            break;
        case 'clear_fields':
            $('#filter_towns').val('').keydown().keyup();
            break;
        case 'show_sidebar':
            toggleSidebar();
            break;
        case 'pin':
            pinTown(e);
            break;
        }
        e.preventDefault();
    };

    function toggleSidebar(force) {
        if (typeof force === 'boolean') {
            sidebar_visible = !force;
        }
        $('#trade_overview_movements_wrapper').toggleClass('expanded', sidebar_visible = !sidebar_visible);
        if (sidebar_visible) {
            that.refreshMovementView();
        } else {
            $('#trade_overview_movements_wrapper').empty();
        }
    }
    this.reInitializeSliders = function () {
        var max, i, id = getSelectedIDs().from;
        if (getSelectedIDs().from === false) {
            return;
        }
        max = ITowns.getTown(id).capacity();
        for (i in resSliders) {
            resSliders[i].setMax(max);
        }
    };

    function getDuration() {
        if (!(selected.from && selected.to)) {
            return;
        }
        Layout.ajaxGet('town_overviews', 'calculate_duration_between_towns', getSelectedIDs(), true, function (return_data) {
            $('#trade_duration span.way_duration').text(return_data.duration);
            $('#trade_duration span.arrival_time').text(return_data.arrival_at);
            $('#trade_duration').fadeIn('fast');
        });
    }
    this.focusHandler = function (e) {
        var cname = 'info_text',
            elem = $(this);
        if (elem.hasClass(cname)) {
            elem.removeClass(cname).val('');
        }
    };
    this.sortHandler = function (e) {
        var s = this.options[this.selectedIndex].value,
            regexp = RegExp(/wood|stone|iron/);

        function compareTowns(a, b) {
            if (regexp.test(s)) {
                a = a.res[s];
                b = b.res[s];
            } else {
                a = a[s];
                b = b[s];
            }
            if (a < b) {
                return -1;
            } else if (a > b) {
                return 1;
            }
            return 0;
        }

        function compareMovements(a, b) {
            if (a[s] < b[s]) {
                return -1;
            } else if (a[s] > b[s]) {
                return 1;
            }
            return 0;
        }
        if (!s.match(/points|pop|storage/)) {
            filteredMovList = movements.sort(compareMovements);
            that.refreshMovementView();
        }
        if (s !== 'arrival') {
            filteredTownList = towns.sort(compareTowns);
            that.refreshView();
        }
    };
    this.keyDownHandler = function (e) {
        window.clearTimeout(timer);
        timer = window.setTimeout(function () {
            that.refreshView();
        }, 250);
    };
    this.keyUpHandler = function (e) {
        var val = this.value.toLowerCase();

        function callbackTowns(element) {
            return (element.name.toLowerCase().match(val));
        }

        function callbackMovements(element) {
            return (element.from.tname.toLowerCase().match(val) || element.to.tname.toLowerCase().match(val));
        }
        filteredTownList = towns.filter(callbackTowns);
        filteredMovList = movements.filter(callbackMovements);
    };
    this.refreshView = function (res_update) {
        if (!filteredTownList) {
            filteredTownList = towns;
        }
        var i = filteredTownList.length,
            html = '',
            town, ids = getSelectedIDs(),
            itowns = ITowns,
            ftown;
        while (i--) {
            ftown = filteredTownList[i];
            town = itowns.getTown(filteredTownList[i].id);
            html += town.render(town_tmpl, {
                'from': ids.from,
                'to': ids.to,
                'in': ftown['in'] || 0,
                'out': ftown.out || 0
            });
        }
        $('#trade_overview_towns').html(html);
        if (sidebar_visible && !res_update) {
            that.refreshMovementView();
        }
    };
    this.refreshMovementView = function () {
        var wrapper = $('#trade_overview_movements_wrapper');
        if (!filteredMovList) {
            filteredMovList = movements;
        }
        var i = filteredMovList.reverse().length,
            html = '',
            mov, now = parseInt(Date.now() / 1E3, 10);
        if (i) {
            while (i--) {
                mov = filteredMovList[i];
                if (mov.arrival >= now) {
                    html += tmpl(mov_tmpl, mov);
                }
            }
        } else {
            html = '<div>' + 'No movements.' + '</div>';
        }
        wrapper.html(html);
        if (sidebar_visible) {
            wrapper.find('span.eta').each(function () {
                $(this).countdown().show();
            });
        }
    };

    function clearSelection() {
        var ids = getSelectedIDs();
        $('#trade_selected_from, #trade_selected_to').each(function () {
            var elem = $(this),
                key = this.id.match(/from|to/).toString(),
                id = ids[key],
                town, ftown;
            if (elem.children().hasClass('pinned')) {
                elem.empty();
                ftown = towns.filter(function (element, index, array) {
                    return (element.id === id);
                })[0];
                town = ITowns.getTown(id).render(town_tmpl, {
                    'from': ids.from,
                    'to': ids.to,
                    'in': ftown['in'] || 0,
                    'out': ftown.out || 0
                });
                dropHandler.call(elem, {
                    helper: $(town),
                    draggable: $()
                });
                pinTown({
                    target: elem.find('a.pin')
                });
            } else {
                elem.empty().removeClass('active').append('<div class="trade_town">');
                delete selected[key];
            }
        });
        $('#trade_duration').hide();
        that.refreshView();
    }

    function swap() {
        var tmp = selected.from;
        if (!(selected.from && selected.to)) {
            return;
        }
        selected.from = selected.to;
        selected.to = tmp;
        $('#trade_selected_from').append(selected.from);
        $('#trade_selected_to').append(selected.to);
    }

    function getSelectedIDs() {
        return {
            from: selected.from ? ~~ (selected.from.attr('id').match(/\d+/)) : false,
            to: selected.to ? ~~ (selected.to.attr('id').match(/\d+/)) : false
        };
    }

    function pinTown(e) {
        $(e.target).toggleClass('active').parent().toggleClass('pinned');
    }
    this.trade = function () {
        var data = getSelectedIDs();
        if (!(isNumber(data.from) & isNumber(data.to))) {
            HumanMessage.error('Draw the two cities into the fields provided for them.');
            return;
        } else if (data.from == data.to) {
            HumanMessage.error('You can\'t send any resources to the same city!');
            return;
        }
        $.each(['wood', 'stone', 'iron'], function (i, res_name) {
            var elem = $('#trade_overview_type_' + res_name);
            data[res_name] = elem.val();
            elem.val('');
        });
        Layout.ajaxPost('town_overviews', 'trade_between_own_town', data, false, function (return_data) {
            if (!return_data.success) {
                return;
            }
            var i, newmove, town = ITowns.getTown(return_data.origin_town_id),
                res = town.resources(),
                diff = return_data.resources,
                cap = town.capacity();
            for (i in diff) {
                res[i] -= diff[i];
                cap -= diff[i];
            }
            town.resources(res).capacity(cap);
            filteredMovList = movements = return_data.movements.reverse();
            newmove = movements.filter(function (element, index, array) {
                return (element.id === return_data.new_trade_movement);
            })[0];
            i = towns.length;
            while (i--) {
                town = towns[i];
                if (town.id === newmove.from.id) {
                    town.out = ++town.out || 1;
                } else if (town.id === newmove.to.id) {
                    town['in'] = ++town['in'] || 1;
                }
            }
            clearSelection();
        });
    };
    init();
};

var TownGroupOverview = {
    hasActiveGroup: false,
    property: '',
    list: null,
    active_list: null,
    temporary_active_group: 0,
    old_temp_active_group: 0,
    move: true,
    sum_active_towns: 0,
    original_target: null,
    town_data: {},
    wnd_handler_id: null,
    reset: function () {
        TownGroupOverview.hasActiveGroup = false;
        TownGroupOverview.property = '';
        TownGroupOverview.list = null;
        TownGroupOverview.active_list = null;
        TownGroupOverview.temporary_active_group = 0;
        TownGroupOverview.old_temp_active_group = 0;
        TownGroupOverview.move = true;
        TownGroupOverview.sum_active_towns = 0;
        TownGroupOverview.original_target = null;
        TownGroupOverview.town_data = {};
        TownGroupOverview.wnd_handler_id = null;
    },
    init: function (wnd_id) {
        TownGroupOverview.reset();
        TownGroupOverview.wnd_handler_id = wnd_id;
        $('.town_draggable').draggable({
            appendTo: 'body',
            helper: function () {
                var clone = $(this).clone();
                var id = '_' + clone.attr('id');
                return clone.attr('id', id);
            },
            drag: function (e, ui) {
                if (TownGroupOverview.original_target == null) {
                    TownGroupOverview.original_target = e.target;
                    if (navigator.appName == "Microsoft Internet Explorer") {
                        $(TownGroupOverview.original_target).attr('style', 'filter:alpha(opacity=50);');
                    } else {
                        $(TownGroupOverview.original_target).attr('style', 'opacity:0.5;');
                    }
                }
            },
            stop: function () {
                $(TownGroupOverview.original_target).removeAttr('style');
                TownGroupOverview.original_target = null;
            }
        });
        $('.town_drop_area_active').droppable({
            accept: '#town_group_all_towns .town_draggable',
            activeClass: 'droppable-active',
            hoverClass: 'droppable-hover',
            drop: function (e, ui) {
                TownGroupOverview.addToGroup(ui.draggable);
            }
        });
        $('.town_drop_area_remaining').droppable({
            accept: '#town_group_active_towns .town_draggable',
            activeClass: 'droppable-active',
            hoverClass: 'droppable-hover',
            drop: function (e, ui) {
                TownGroupOverview.removeFromGroup(ui.draggable);
            }
        });
        TownGroupOverview.makeListsFromDOM();
        TownGroupOverview.setUnitPopups();
        TownGroupOverview.sortTownsBy('name', false);
        $('.sort_icon_all.town_name').addClass('active');
        $('.sort_icon_active').bind('click', function () {
            TownGroupOverview.sortTownsBy(($(this).attr('class')).match(/town_\w+/g)[0], true);
            $('.sort_icon_active').removeClass('active');
            $(this).addClass('active');
        });
        $('.sort_icon_all').bind('click', function () {
            TownGroupOverview.sortTownsBy(($(this).attr('class')).match(/town_\w+/g)[0], false);
            $('.sort_icon_all').removeClass('active');
            $(this).addClass('active');
        });
        $('.overview_type_icon').bind('click', function () {
            TownGroupOverview.setOverviewType(($(this).attr('class')).replace(/(overview_type_icon\s)|(\sactive)/g, ''), false);
        });
        $('.sort_icon_all.town_name').mousePopup(new MousePopup('Sort by city name'));
        $('.sort_icon_all.town_points').mousePopup(new MousePopup('Sort by points'));
        $('.sort_icon_all.town_population').mousePopup(new MousePopup('Sort by free population'));
        $('.sort_icon_active.town_name').mousePopup(new MousePopup('Sort by city name'));
        $('.sort_icon_active.town_points').mousePopup(new MousePopup('Sort by points'));
        $('.sort_icon_active.town_population').mousePopup(new MousePopup('Sort by free population'));
        $('.overview_type_icon.show_resources').mousePopup(new MousePopup('Show resources in the cities'));
        $('.overview_type_icon.show_units').mousePopup(new MousePopup('Show units in the cities'));
        $('.rename_town_group').mousePopup(new MousePopup('Rename group'));
        $('.rename_town_confirm').mousePopup(new MousePopup('Save'));
        $('.rename_town_cancel').mousePopup(new MousePopup('Cancel'));
        $('.select_town_group').mousePopup(new MousePopup('Activate group'));
        $('.delete_town_group').mousePopup(new MousePopup('Delete group'));
        $('.storage').mousePopup(new MousePopup('Warehouse'));
        $('.population_info').mousePopup(new MousePopup('Free population'));
        $('.town_population_sort').mousePopup(new MousePopup('Sort by free population'));
        $(document).bind('ITowns.TownGroups.removed', TownGroupOverview.removeFromGroupEvent);
        $(document).bind('ITowns.TownGroups.added', TownGroupOverview.addToGroupEvent);
    },
    makeListsFromDOM: function () {
        TownGroupOverview.list = $.makeArray($('#town_group_all_towns .town_item'));
        TownGroupOverview.active_list = $.makeArray($('#town_group_active_towns .town_item'));
    },
    setUnitPopups: function () {
        $.each(GameData.units, function (unit) {
            $(".unit_" + unit).setPopup(unit);
        });
    },
    setOverviewType: function (type) {
        var data = {};
        data.overview_type = type;
        if (this.town_data[type]) {
            TownGroupOverview.update_town_data(this.town_data[type], type);
            return;
        }
        Layout.ajaxPost('town_group_overviews', 'set_overview_type', data, false, function (return_data) {
            TownGroupOverview.update_town_data(return_data.towns, type);
            TownGroupOverview.town_data[type] = return_data.towns;
            TownGroupOverview.setUnitPopups();
        }, {}, 'set_overview_type');
    },
    update_town_data: function (data, type) {
        $.each(data, function (town_id, content) {
            $('#ov_town_' + town_id + ' .box_content').html('<div>' + content + '</div>');
        });
        $('.overview_type_icon').removeClass('active');
        $('#sort_groups .' + type).addClass('active');
    },
    removeFromGroup: function (town_element) {
        var data = {};
        data.town_id = town_element[0].id.replace("ov_town_", "");
        data.group_id = TownGroupOverview.temporary_active_group;
        ITowns.townGroupsRemoveFromGroup(data.group_id, data.town_id);
    },
    removeFromGroupEvent: function (evt, return_data) {
        TownGroupOverview.moveRight(return_data);
        if ($('#town_group_active_towns').children().length == 0) {
            $('#town_group_id_' + return_data.group_id).find('a.select_town_group').remove();
            if (return_data.is_active) {
                TownGroupOverview.setActiveTownGroup(0, '', '', false);
            }
        }
        if ($('#town_group_all_towns').children().length == 1) {
            $('#sort_icons_all_towns .hide').removeClass('hide').addClass('show');
        }
    },
    addToGroup: function (town_element) {
        var data = {};
        data.town_id = parseInt(town_element[0].id.replace("ov_town_", ""), 10);
        data.group_id = TownGroupOverview.temporary_active_group;
        if (data.group_id == 0) {
            HumanMessage.error('No group has been selected!');
            return;
        }
        ITowns.townGroupsAddToGroup(data.group_id, data.town_id);
    },
    addToGroupEvent: function (evt, return_data) {
        TownGroupOverview.moveLeft(return_data);
        if (TownGroupOverview.sum_active_towns > 0) {
            $('#sort_icons_active_group_towns .hide').removeClass('hide').addClass('show');
            if (!$('#town_group_id_' + return_data.group_id).find('a.select_town_group').length) {
                $('#town_group_id_' + return_data.group_id).append('<a class="select_town_group confirm" href="#" onclick="TownGroupOverview.setActiveTownGroup(' + return_data.group_id + ', \'town_group_overviews\', \'\'); return false"></a>');
            }
        }
        if ($('#town_group_all_towns').children().length == 0 && $('.town_group_active.show').length > 0) {
            Layout.showHint('Information', 'You have currently arranged all your cities into one city group. If you receive new cities, they will not appear in the overview or the city list. To display the new city in the overviews, you can either add it to the group manually or deselect the active group.');
        }
    },
    moveLeft: function (town_data) {
        if ($('#town_group_active_towns').find('#ov_town_' + town_data.town_id).length != 0) {
            return;
        }
        var town = $('#ov_town_' + town_data.town_id);
        TownGroupOverview.insertTownInto(town, $('#town_group_active_towns'));
        TownGroupOverview.sum_active_towns++;
        TownGroupOverview.makeListsFromDOM();
        if ($('#town_group_all_towns').children().length == 0) {
            $('#sort_icons_all_towns .show').removeClass('show').addClass('hide');
        }
        $('.sort_icon_active').removeClass('active');
    },
    moveRight: function (town_data) {
        var town = $('#ov_town_' + town_data.town_id + '');
        TownGroupOverview.insertTownInto(town, $('#town_group_all_towns'));
        TownGroupOverview.sum_active_towns--;
        TownGroupOverview.makeListsFromDOM();
        if ($('#town_group_active_towns').children().length == 0) {
            $('#sort_icons_active_group_towns .show').removeClass('show').addClass('hide');
        }
        $('.sort_icon_all').removeClass('active');
    },
    insertTownInto: function (town, list) {
        town.appendTo(list);
        TownGroupOverview.debugStats();
    },
    setTemporaryActiveGroup: function (town_id_arr) {
        var tmp_obj_hide_to_show = null;
        var tmp_obj_show_to_hide = null;
        var tmp_title = '';
        TownGroupOverview.cleanupActiveTowns(town_id_arr);
        TownGroupOverview.cleanupRemainingTowns(town_id_arr);
        TownGroupOverview.sum_active_towns = countProperties(town_id_arr);
        if (TownGroupOverview.old_temp_active_group != 0) {
            tmp_obj_show_to_hide = $('#town_group_id_' + TownGroupOverview.old_temp_active_group + ' .show');
            tmp_obj_hide_to_show = $('#town_group_id_' + TownGroupOverview.old_temp_active_group + ' .hide');
            tmp_obj_show_to_hide.removeClass('show').addClass('hide');
            tmp_obj_hide_to_show.removeClass('hide').addClass('show');
        }
        if (TownGroupOverview.temporary_active_group != 0) {
            tmp_obj_show_to_hide = $('#town_group_id_' + TownGroupOverview.temporary_active_group + ' .show');
            tmp_obj_hide_to_show = $('#town_group_id_' + TownGroupOverview.temporary_active_group + ' .hide');
            tmp_obj_show_to_hide.removeClass('show').addClass('hide');
            tmp_obj_hide_to_show.removeClass('hide').addClass('show');
            tmp_title = $('#town_group_id_' + TownGroupOverview.temporary_active_group + ' .show').text();
            $('#active_town_list_head').text('Cities from %s'.replace('%s', tmp_title));
            if ($('#town_group_id_' + TownGroupOverview.temporary_active_group + ' .select_town_group').length > 0) {
                $('#sort_icons_active_group_towns .hide').removeClass('hide').addClass('show');
            }
        }
    },
    cleanupActiveTowns: function (town_id_arr) {
        var active_towns = $('#town_group_active_towns .town_item');
        $.each(active_towns, function (nr) {
            var town_id = this.id.replace("ov_town_", "");
            var move = true;
            $.each(town_id_arr, function (id) {
                if (this.id == town_id) {
                    move = false;
                }
            });
            if (move) {
                TownGroupOverview.moveRight({
                    'town_id': town_id
                });
            }
        });
        if ($('#town_group_active_towns').children().length == 0) {
            $('#sort_icons_active_group_towns .show').removeClass('show').addClass('hide');
        }
        Sort.sortBy(null);
        TownGroupOverview.sortTowns(false);
    },
    cleanupRemainingTowns: function (town_id_arr) {
        var rem_towns = $('#town_group_all_towns .town_item');
        $.each(rem_towns, function (nr) {
            var town_id = this.id.replace("ov_town_", "");
            $.each(town_id_arr, function (id) {
                if (this.id == town_id) {
                    TownGroupOverview.moveLeft({
                        'town_id': town_id
                    });
                }
            });
        });
        Sort.sortBy(null);
        TownGroupOverview.sortTowns(true);
    },
    unsetActiveSort: function (isMovedLeft) {
        if (isMovedLeft) {
            $('.sort_icon_active').removeClass('active');
        } else {
            $('.sort_icon_all').removeClass('active');
        }
    },
    sortTowns: function (isMovedLeft) {
        var tmp = TownGroupOverview.property != '' ? TownGroupOverview.property : 'town_name';
        TownGroupOverview.sortTownsBy(tmp, isMovedLeft);
    },
    sortTownsBy: function (_property, isMovedLeft) {
        this.property = _property;
        if (isMovedLeft) {
            if (Sort.sortedBy(_property)) {
                TownGroupOverview.active_list = TownGroupOverview.active_list.reverse();
            } else {
                Sort.sortBy(_property);
                TownGroupOverview.active_list = Sort.qsort(TownGroupOverview.active_list);
            }
            $('#town_group_active_towns').append(TownGroupOverview.active_list);
        } else {
            if (Sort.sortedBy(_property)) {
                TownGroupOverview.list = TownGroupOverview.list.reverse();
            } else {
                Sort.sortBy(_property);
                TownGroupOverview.list = Sort.qsort(TownGroupOverview.list);
            }
            $('#town_group_all_towns').append(TownGroupOverview.list);
        }
    },
    setActiveTownGroup: function (group_id, controller_name, action_name) {
        if ($('#town_group_overview .town_group_active.show').length == 0 && group_id == 0) {
            return;
        }
        if (controller_name == 'town_overviews') {
            var handler = function (event, data) {
                    TownGroupOverview.toggleTownGroupList(controller_name, action_name);
                    var w = Layout.wnd.GetByID(TownGroupOverview.wnd_handler_id);
                    w.requestContentGet('town_group_overviews', 'town_group_overview', {});
                    $(document).unbind('ITowns.TownGroups.setActiveTownGroup', handler);
                };
            $(document).bind('ITowns.TownGroups.setActiveTownGroup', handler);
            ITowns.setActiveTownGroup(group_id);
        } else if (controller_name == 'town_group_overviews') {
            if ($('#town_group_id_' + group_id + ' .town_group_active').length == 0) {
                var handler = function (event, data) {
                        TownGroupOverview.setTemporaryActiveTownGroup(data.town_group_id);
                        var tg_img = $('.show .img_active_town_group');
                        if (tg_img.length == 0) {
                            tg_img = $('<img class="img_active_town_group" src="http://cdn.grepolis.com/images/game/overviews/active_group.png" alt="A - " height="14" width="14" />');
                        }
                        $('.town_group_active').removeClass('town_group_active').addClass('town_group_inactive');
                        $('#town_group_id_' + data.town_group_id + ' .town_group_inactive').removeClass('town_group_inactive').addClass('town_group_active');
                        tg_img.prependTo($('#town_group_id_' + data.town_group_id + ' .town_group_active'));
                        $('.town_group_inactive .img_active_town_group').remove();
                        if ($('#town_group_id_' + data.town_group_id + ' .show .bold').length > 0) {
                            var active_hide = $('#town_group_id_' + data.town_group_id + ' .show');
                            var active_show = $('#town_group_id_' + data.town_group_id + ' .hide');
                            active_hide.removeClass('show').addClass('hide');
                            active_show.removeClass('hide').addClass('show');
                        }
                        if ($('#town_list').is(':visible')) {
                            $('#town_list').hide().empty();
                        }
                        if (data.town_group_id == 0) {
                            var w = Layout.wnd.GetByID(TownGroupOverview.wnd_handler_id);
                            w.requestContentGet('town_group_overviews', 'town_group_overview', {});
                        }
                        $(document).unbind('ITowns.TownGroups.setActiveTownGroup', handler);
                    };
                $(document).bind('ITowns.TownGroups.setActiveTownGroup', handler);
                ITowns.setActiveTownGroup(group_id);
            }
        } else {
            var handler = function (event, data) {
                    TownGroupOverview.toggleTownGroupList(controller_name, action_name);
                    $(document).unbind('ITowns.TownGroups.setActiveTownGroup', handler);
                };
            $(document).bind('ITowns.TownGroups.setActiveTownGroup', handler);
            ITowns.setActiveTownGroup(group_id);
        }
        return false;
    },
    toggleTownGroupList: function (controller_name, action_name) {
        if (Layout.town_group_list_toggle) return;
        var list_element = $('#town_list');
        if (list_element.is(':visible')) {
            list_element.hide().empty();
        } else {
            Layout.town_group_list_toggle = true;
            list_element.detach();
            Layout.ajaxGet('town_group_overviews', 'get_selectable_town_groups', {
                'controller_name': controller_name,
                'action_name': action_name
            }, false, function (data) {
                var town_groups = data.town_groups;
                var list = $('<ul></ul>');
                $('<div id="town_list_top"></div>').appendTo(list);
                $.each(town_groups, function (nr) {
                    if (this.active) {
                        TownGroupOverview.has_active_group = true;
                        $('<li><img src="http://cdn.grepolis.com/images/game/overviews/active_group.png" alt="A - " height="14" width="14" /> <span class="bold">' + this.name + '</span></li>').appendTo(list);
                    } else {
                        $('<li><a href="#" onclick="return TownGroupOverview.setActiveTownGroup(' + this.id + ', \'' + data.controller_name + '\', \'' + data.action_name + '\')">' + '' + this.name + '</a></li>').appendTo(list);
                    }
                });
                $('<li>&nbsp;</li>').appendTo(list);
                if (TownGroupOverview.has_active_group) {
                    $('<li><a href ="#" onclick="return TownGroupOverview.setActiveTownGroup(0, \'' + data.controller_name + '\', \'' + data.action_name + '\')">' + 'Deselect group' + '</a></li>').appendTo(list);
                }
                $('<li><a href ="#" onclick="return TownGroupOverview.gotoTownGroupOverview()">' + 'Manage groups' + '</a></li>').appendTo(list);
                $('<div id="town_list_bottom"></div>').appendTo(list);
                list.appendTo(list_element);
                $('#box').append(list_element);
                list_element.show();
                delete Layout.town_group_list_toggle;
            });
        }
    },
    deleteTownGroup: function (id, town_group_name, active) {
        var check = confirm('Really delete the group %s?'.replace('%s', town_group_name));
        if (check) {
            Layout.ajaxPost('town_group_overviews', 'delete_town_group', {
                town_group_id: id,
                town_group_name: town_group_name,
                active: active
            }, false, function (data) {
                if (data.success) {
                    $('#town_group_id_' + id + '').remove();
                    if (data.active || data.town_group_id == TownGroupOverview.temporary_active_group) {
                        TownGroupOverview.has_active_group = false;
                        TownGroupOverview.temporary_active_group = 0;
                        TownGroupOverview.cleanupActiveTowns({});
                        $('#active_town_list_head').text('No group has been selected');
                        $('#sort_icons_active_group_towns .show').removeClass('show').addClass('hide');
                    }
                    var sum_groups = $('#overview_town_group_list').children().length;
                    var list = $('#town_group_overview_dummy');
                    if (sum_groups == data.max_sum_groups - 1) {
                        var elem = $('<ul class="game_list" id="town_group_add_new_group"></ul>');
                        elem.append('<li><form id="town_group_name" class="bold" action=""><span id="town_grop_name_span_text"><a href="javascript:void(0)" onclick="TownGroupOverview.addTownGroup()" id="add_town_group_href">' + 'Add new group' + '</a></span><span id="town_group_name_span_input" style="display:none"><input type="text" id="town_group_name_input" value="" maxlength="20" size="15" /><img src="http://cdn.grepolis.com/images/game/layout/town_name_save.png" alt="" id="save_town_group_name" onclick="TownGroupOverview.saveTownGroupName()" style="cursor:pointer" /></span></form></li>');
                        elem.appendTo(list);
                    } else if (sum_groups == 0) {
                        $('#town_group_overview_head').text('No group present');
                    }
                    $(document).trigger('ITowns.refetch');
                    GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_TOWN_OVERVIEWS).reloadContent();
                }
            }, {}, 'delete_town_group');
        }
    },
    saveTownGroupName: function () {
        var town_group_name = $('#town_group_name_input').val();
        Layout.ajaxPost('town_group_overviews', 'add_town_group', {
            town_group_name: town_group_name
        }, false, function (data) {
            if (data.success) {
                var list_element = $('#overview_town_group_list');
                var list = $('<li class="town_group_name" id="town_group_id_' + data.town_group_id + '"><div class="town_group_inactive show"><a href="#" onclick="TownGroupOverview.setTemporaryActiveTownGroup(' + data.town_group_id + ', \'\'' + ', \'\'' + ', ' + false + ')">' + data.town_group_name + '</a></div>' + '<div class="town_group_inactive hide"><span class="bold">' + data.town_group_name + '</span></div><a class="cancel delete_town_group" href="#" onclick="TownGroupOverview.deleteTownGroup(' + data.town_group_id + ', \'' + data.town_group_name + '\', false)">' + '</a><a class="rename rename_town_group" href="#" onclick="TownGroupOverview.renameTownGroup(' + data.town_group_id + ', \'' + data.town_group_name + '\')">' + '</a></li>');
                list.appendTo(list_element);
                var sum_groups = $(list_element).children().length;
                if (sum_groups >= data.max_sum_groups) {
                    $('#town_group_add_new_group').remove();
                }
                if (sum_groups == 1) {
                    $('#town_group_overview_head').text('Groups');
                }
                $('#town_group_name_span_input').css('display', 'none');
                if (data.show_hint) {
                    var hint = 'You have now created your first city group. You can add cities after you have activated it. As soon as you have activated a group, only cities of this group are shown in the overview. In order to see all cities in the overview, the active group must be deselected. You can do this in the dropdown \"group list\" in the fast navigation.';
                    Layout.showHint('Game tip:', hint);
                }
                $(document).trigger('ITowns.refetch');
                GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_TOWN_OVERVIEWS).reloadContent();
            }
        }, {}, 'add_town_group');
    },
    renameTownGroupName: function () {
        var town_group_rename_id = $('#town_group_rename_input_id').val();
        var town_group_rename_name = $('#town_group_rename_input_name').val();
        Layout.ajaxPost('town_group_overviews', 'rename_town_group', {
            town_group_id: town_group_rename_id,
            town_group_name: town_group_rename_name
        }, false, function (data) {
            if (data.success) {
                $('#town_group_rename_input_id').attr('value', '');
                $('#town_group_rename_input_name').attr('value', '');
                $('#town_group_options_rename').hide();
                $('#town_group_options_add').show();
                $(document).trigger('ITowns.refetch');
                GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_TOWN_OVERVIEWS).reloadContent();
            }
        }, {}, 'rename_town_group');
    },
    renameCancel: function () {
        $('#town_group_rename_input_name').attr('value', '');
        $('#town_group_rename_input_id').attr('value', '');
        $('#town_group_options_rename').hide();
        $('#town_group_options_add').show();
    },
    renameTownGroup: function (town_group_rename_id, town_group_rename_name) {
        $('#town_group_options_add').hide();
        $('#town_group_options_rename').show();
        $('#town_group_rename_input_id').attr('value', town_group_rename_id);
        $('#town_group_rename_input_name').attr('value', town_group_rename_name);
    },
    addTownGroup: function () {
        $('#town_group_name_span_input').css('display', '');
        $('#town_group_name').bind('submit', TownGroupOverview.saveTownGroupName);
    },
    setTemporaryActiveTownGroup: function (group_id) {
        TownGroupOverview.old_temp_active_group = TownGroupOverview.temporary_active_group;
        TownGroupOverview.temporary_active_group = group_id;
        Layout.ajaxGet('town_group_overviews', 'get_town_ids_by_group', {
            'group_id': group_id
        }, false, TownGroupOverview.editTownGroupEvent);
    },
    editTownGroupEvent: function (data) {
        if (data.t_token) {
            delete data.t_token
        }
        TownGroupOverview.setTemporaryActiveGroup(data);
    },
    gotoTownGroupOverview: function () {
        var w = Layout.wnd.GetByID(TownGroupOverview.wnd_handler_id);
        w.requestContentGet('town_group_overviews', 'town_group_overview', {});
    },
    debugStats: function () {}
};

var PlayerHint = {
    confirm: function (hint_type) {
        var confirmed = hint_type == "age_of_wonder" || confirm('Disable this message?');
        if (confirmed) {
            Layout.ajaxPost('player_hint', 'disable', {
                player_hint: hint_type
            }, true, function (data) {});
            return true;
        }
        return false;
    },
    close: function () {
        $('#player_hint').stop(true, true).fadeOut();
    },
    disable: function (player_hint, achievement_id, award_id, award_level, award_name) {
        var ajax = new gpAjax(Layout, true);
        if (confirm('Disable this message?')) {
            this.close();
            if (achievement_id > 0) {
                Layout.ajaxPost('player_achievement', 'disable', {
                    achievement_id: achievement_id
                }, true, function (data) {
                    PlayerHint.close();
                });
            } else {
                Layout.ajaxPost('player_hint', 'disable', {
                    player_hint: player_hint
                }, true, function (data) {
                    PlayerHint.close();
                });
            }
            return false;
        }
    }
};

jQuery.fn.extend({
    setPopup: function (popup_type) {
        PopupFactory.bindNewPopupTo(this, popup_type);
    }
});
var PopupFactory = {
    texts: {},
    init: function () {
        var ts = {
            storage_info: '<b>' + "Warehouse" + '</b>',
            population_info: '<b>' + "Free population" + '</b>',
            next_city: 'Next city',
            prev_city: 'Last city',
            city_list: 'City list',
            change_city_name: 'Change name of city',
            save_city_name: 'Save name of city',
            unit_type_hack: '<h4>' + 'Blunt weapon' + '</h4>',
            unit_type_pierce: '<h4>' + 'Sharp weapon' + '</h4>',
            unit_type_distance: '<h4>' + 'Distance weapon' + '</h4>',
            party: 'City festival',
            games: 'Olympic Games',
            theater: 'Theater plays',
            triumph: 'Victory procession',
            cast_spell_on_own_town: 'Cast a spell on your own city'
        };
        var pfs = [{
            i: 'curator',
            t: 'Administrator',
            d: PopupFactory.texts.curator_info
        }, {
            i: 'trader',
            t: 'Merchant',
            d: PopupFactory.texts.trader_info
        }, {
            i: 'priest',
            t: 'High Priestess',
            d: PopupFactory.texts.priest_info
        }, {
            i: 'commander',
            t: 'Commander',
            d: PopupFactory.texts.commander_info
        }, {
            i: 'captain',
            t: 'Captain',
            d: PopupFactory.texts.captain_info
        }];
        var i = pfs.length;
        while (i--) {
            ts[pfs[i].i + '_info'] = '<div class="premium_advisor_image advisor_popup ' + pfs[i].i + '" ></div><div class="premium_advisor_popup_text"><b>' + pfs[i].t + '</b><br />' + pfs[i].d + '</div>';
        }
        this.addTexts(ts);
        var powers_values = {};
        $.each(GameData.powers, function (key, power) {
            powers_values[key] = '<div class="temple_power_popup">' + '<div class="temple_power_popup_image" style="background: url(http://cdn.grepolis.com/images/game/temple/power_' + power.id + '.png) 0 -86px no-repeat;"/>' + '<div class="temple_power_popup_info">' + '<h4>' + power.name + '</h4>' + '<p>' + power.description + '</p>' + '<b>' + power.effect + '</b><br />' + '<p><img src="http://cdn.grepolis.com/images/game/res/favor.png" alt="" />' + '%1 favor'.replace('%1', power.favor) + '</p>' + '</div>' + '</div>';
        });
        this.addTexts(powers_values);
        var unit_values = {};
        $.each(GameData.units, function (key, unit) {
            unit_values[key] = unit.name;
            if (PopupFactory.isGroundUnit(unit)) {
                unit_values[key + '_details'] = '<div class="temple_unit_popup">' + '<h4>' + unit.name + '</h4>' + '<img src="http://cdn.grepolis.com/images/game/units/' + unit.id + '_90x90.jpg" alt="' + unit.name + '" />' + '<div class="temple_unit_popup_info">' + '<table id="unit_order_unit_info" border="1" style="font-weight: bold">' + '<tr>' + '<td><div id="unit_order_att_' + unit.attack_type + '" />' + unit.attack + '</td>' + '<td><div id="unit_order_def_hack" />' + unit.def_hack + '</td>' + '</tr>' + '<tr>' + '<td><div id="unit_order_speed" />' + unit.speed + '</td>' + '<td><div id="unit_order_def_pierce" />' + unit.def_pierce + '</td>' + '</tr>' + '<tr>' + '<td><div id="unit_order_booty" />' + (unit.booty ? unit.booty : '0') + '</td>' + '<td><div id="unit_order_def_distance" />' + unit.def_distance + '</td>' + '</tr>' + '</table>' + '<p>' + unit.description + '</p>' + '</div>' + '</div>';
            } else {
                unit_values[key + '_details'] = '<div class="temple_unit_popup">' + '<h4>' + unit.name + '</h4>' + '<img src="http://cdn.grepolis.com/images/game/units/' + unit.id + '_90x90.jpg" alt="' + unit.name + '" />' + '<div class="temple_unit_popup_info">' + '<table id="unit_order_unit_info" border="1" style="font-weight: bold">' + '<tr>' + '<td><div id="unit_order_attack" />' + unit.attack + '</td>' + '<td><div id="unit_order_defense" />' + unit.defense + '</td>' + '</tr>' + '<tr>' + '<td><div id="unit_order_speed" />' + unit.speed + '</td>' + '<td><div id="unit_order_transport" />' + unit.capacity + '</td>' + '</tr>' + '</table>' + '<p>' + unit.description + '</p>' + '</div>' + '</div>';
            }
        });
        this.addTexts(unit_values);
    },
    updateProductionPopup: function (type, data) {
        var popup_html;
        switch (type) {
        case 'wood':
            popup_html = '<h4>' + 'Wood' + '</h4>';
            popup_html += '<span id="production_wood_text">' + 'Production per hour' + ': ' + (data.wood || 'n/a') + '</span>';
            popup_html += '<br/><span>' + 'Warehouse size' + ': ' + (data.storage_volume || 'n/a') + '</span>';
            popup_html += ('wood' == (data.resource_rare || '')) ? '<br/>' + 'This island yields less wood than the others.' : '';
            popup_html += ('wood' == (data.resource_plenty || '')) ? '<br/>' + 'Trees grow more quickly on this island than on other islands, which means that your timber camp can produce more wood.' : '';
            popup_html += (('wood' == (data.resource_plenty || '')) || ('wood' == (data.resource_rare || ''))) ? 'This can be seen from building level 20 onwards.' : '';
            this.addTexts({
                'wood_production': popup_html
            });
            break;
        case 'stone':
            popup_html = '<h4>' + 'Rock' + '</h4>';
            popup_html += '<span id="production_stone_text">' + 'Production per hour' + ': ' + (data.stone || 'n/a') + '</span>';
            popup_html += '<br/><span>' + 'Warehouse size' + ': ' + (data.storage_volume || 'n/a') + '</span>';
            popup_html += ('stone' == (data.resource_rare || '')) ? '<br/>' + 'There are no especially large stone deposits on this island, which means that your quarry doesn\'t work as efficiently as others.' : '';
            popup_html += ('stone' == (data.resource_plenty || '')) ? '<br/>' + 'You will profit from a high rock production on this island.' : '';
            popup_html += (('stone' == (data.resource_plenty || '')) || ('stone' == (data.resource_rare || ''))) ? 'This can be seen from building level 20 onwards.' : '';
            this.addTexts({
                'stone_production': popup_html
            });
            break;
        case 'iron':
            popup_html = '<h4>' + 'Silver coins' + '</h4>';
            popup_html += '<span id="production_stone_text">' + 'Production per hour' + ': ' + (data.iron || 'n/a') + '</span>';
            popup_html += '<br/><span>' + 'Warehouse size' + ': ' + (data.storage_volume || 'n/a') + '</span>';
            popup_html += ('iron' == (data.resource_rare || '')) ? '<br/>' + 'The silver deposits on this island are fairly rare, which means that you have a slightly lower silver production.' : '';
            popup_html += ('iron' == (data.resource_plenty || '')) ? '<br/>' + 'The mountains on this island have enormous silver deposits, which means that your silver mine can extract more silver.' : '';
            popup_html += (('iron' == (data.resource_plenty || '')) || ('iron' == (data.resource_rare || ''))) ? 'This can be seen from building level 20 onwards.' : '';
            this.addTexts({
                'iron_production': popup_html
            });
            break;
        case 'favor':
            var favor;
            data.favors = data.favors || {};
            popup_html = '<h4>' + 'Favor' + '</h4>';
            popup_html += '<ul>';
            for (var key in data.favors) {
                favor = data.favors[key];
                if (data.favors.hasOwnProperty(key) && favor.production) {
                    popup_html += '<li>' + favor.god + ': ' + favor.current + ' - ' + 'Production per hour' + ': ' + favor.production + '</li>';
                }
            }
            popup_html += '</ul>';
            this.addTexts({
                'favor_production': popup_html
            });
            break;
        default:
            break;
        }
    },
    addTexts: function (texts) {
        jQuery.extend(this.texts, texts);
    },
    bindNewPopupTo: function (element, popup_type) {
        if (this.texts[popup_type] == undefined) {}
        element.mousePopup(new MousePopup(this.texts[popup_type]));
    },
    isGroundUnit: function (unit) {
        return unit.capacity == undefined;
    }
}

var Quickbar = {
    data: {},
    layoutInitializeQuickbar: function (data) {
        var timeout = 250;
        var toolbar_toggle_menus = $("#toolbar li.toolbar_toggle_menu");
        toolbar_toggle_menus.each(function () {
            var elm = $(this),
                menu = elm.children('div'),
                original = elm;
            if (Layout instanceof GPLayout) {
                elm.bind({
                    'mouseenter': function (ev) {
                        menu.appendTo('body').css({
                            left: elm.offset().left,
                            top: (elm.offset().top + elm.height())
                        }).show();
                        window.clearTimeout(menu.attr('class'));
                        menu.attr('class', '');
                    },
                    'mouseleave': function (ev) {
                        var offset = menu.offset(),
                            elm_off_x = offset.left,
                            elm_off_y = offset.top;
                        if (ev.clientX < elm_off_x || ev.clientX > elm_off_x + elm.width() || ev.clientY < elm_off_y) {
                            menu.hide().attr('class', window.setTimeout(function () {
                                menu.hide().appendTo(original);
                                menu.attr('class', '');
                            }, timeout));
                        }
                        if (menu.hover()) {
                            menu.hover(function () {}, function () {
                                menu.hide().attr('class', window.setTimeout(function () {
                                    menu.hide().appendTo(original);
                                    menu.attr('class', '');
                                }, timeout));
                            });
                        }
                    }
                });
            }
        });
        $('#quickbar_properties').mousePopup(new MousePopup('Edit quick bar'));
    },
    initIconChooser: function () {
        this.selectCurrentIcon();
        var toolbar_icon_div = $('#toolbar_icons div');
        toolbar_icon_div.click(function () {
            var elm = $(this);
            $('#toolbar_item_image').val($(elm.children('img')).attr('src'));
            toolbar_icon_div.removeClass('selected');
            elm.addClass('selected');
        });
    },
    selectCurrentIcon: function () {
        var toolbar_icons_img = $('#toolbar_icons img');
        var i = toolbar_icons_img.length;
        while (i--) {
            if ($(toolbar_icons_img[i]).attr('src') == $('#toolbar_item_image').val()) {
                $($('#toolbar_icons div')[i]).addClass('selected');
            } else {
                $($('#toolbar_icons div')[i]).removeClass('selected');
            }
        }
    },
    setData: function (data) {
        Quickbar.data = data;
    },
    initialize: function (data) {
        var that = Layout.wnd.getOpenFirst(Layout.wnd.TYPE_PLAYER_SETTINGS);
        var quickbar_elm = $('#quickbar');
        Quickbar.setData(data);
        $('#enable_quickbar').click(function () {
            var input = $(this);
            var enabled = input.attr('checked') ? true : false;
            that.ajaxRequestPost('quickbar', 'toggle_quickbar', {
                quickbar_enabled: enabled
            }, function (window, data) {
                that.setContent2(data.html);
                Quickbar.refresh(data);
            });
        });
        quickbar_elm.find('a').click(function () {
            var id = $(this).parent('li').attr('id').replace(/id_/, '');
            var item = Quickbar.data[id];
            Quickbar.show_edit_form(item);
        });
        quickbar_elm.sortable({
            update: function (event, ui) {
                var sort_array_unformated = quickbar_elm.sortable('toArray');
                var sort_array = [];
                $.each(sort_array_unformated, function (i, id) {
                    sort_array[i] = ~~ (id.replace(/id_/, ''));
                });
                that.ajaxRequestPost('quickbar', 'resort', {
                    sort_array: sort_array
                }, function (window, data) {
                    that.setContent2(data.html);
                    Quickbar.refresh(data);
                });
            }
        });
        var toolbar_item_id = $('#toolbar_item_id'),
            toolbar_item_name = $('#toolbar_item_name'),
            toolbar_item_image = $('#toolbar_item_image'),
            toolbar_item_url = $('#toolbar_item_url'),
            edit_toolbar_item = $('#edit_toolbar_item'),
            add_data = $('#add_data'),
            item_url = $('#item_url'),
            save_data = $('#save_data'),
            submenu = $('#submenu');
        $('#add_item_show').click(function () {
            toolbar_item_id.val('');
            toolbar_item_name.val('');
            toolbar_item_image.val('');
            toolbar_item_url.val('');
            edit_toolbar_item.fadeIn('fast');
            add_data.show();
            item_url.show();
            save_data.hide();
            submenu.hide();
        });
        add_data.click(function () {
            var item = {
                name: toolbar_item_name.val(),
                url: toolbar_item_url.val(),
                image: toolbar_item_image.val()
            };
            if (submenu.html() != '') {
                item.submenu = edit_toolbar_item.serializeArray();
            }
            that.ajaxRequestPost('quickbar', 'add_item', item, function (window, data) {
                HumanMessage.success('The menu item was added.');
                that.setContent2(data.html);
                Quickbar.refresh(data);
            });
        });
        $('#add_submenu_show').click(function () {
            Quickbar.show_edit_form({
                'name': '',
                'image': ''
            });
            toolbar_item_url.val('');
            add_data.show();
            item_url.hide();
            save_data.hide();
        });
        save_data.click(function () {
            var item = {
                id: toolbar_item_id.val(),
                name: toolbar_item_name.val(),
                url: toolbar_item_url.val(),
                image: toolbar_item_image.val()
            };
            if (submenu.html() != '') {
                item.submenu = edit_toolbar_item.serializeArray();
            }
            that.ajaxRequestPost('quickbar', 'edit_item', item, function (window, data) {
                HumanMessage.success('The quick bar has been edited successfully.');
                that.setContent2(data.html);
                Quickbar.refresh(data);
            });
        });
        $('#remove_data').click(function () {
            var id = toolbar_item_id.val();
            var confirm_delete = confirm('Are you sure that you would like to remove this menu item?');
            if (!confirm_delete) {
                return;
            }
            that.ajaxRequestPost('quickbar', 'remove_item', {
                'id': id
            }, function (window, data) {
                HumanMessage.success('The menu item was removed.');
                that.setContent2(data.html);
                Quickbar.refresh(data);
            });
        });
        $('#reset_quickbar').click(function () {
            var confirm_reset = confirm('Are you sure that you want to reset the quick bar?');
            if (!confirm_reset) {
                return;
            }
            that.ajaxRequestPost('quickbar', 'reset_quickbar', {}, function (window, data) {
                HumanMessage.success('The quick bar has been reset.');
                that.setContent2(data.html);
                Quickbar.refresh(data);
            });
        });
    },
    'show_edit_form': function (item) {
        var toolbar_item_id = $('#toolbar_item_id'),
            toolbar_item_name = $('#toolbar_item_name'),
            toolbar_item_image = $('#toolbar_item_image'),
            toolbar_item_url = $('#toolbar_item_url'),
            edit_toolbar_item = $('#edit_toolbar_item'),
            add_data = $('#add_data'),
            item_url = $('#item_url'),
            save_data = $('#save_data'),
            submenu = $('#submenu'),
            toolbar_icons = $('#toolbar_icons');
        add_data.hide();
        save_data.show();
        toolbar_item_id.val(item.id);
        toolbar_item_name.val(item.name);
        toolbar_item_image.val(item.image);
        if (item.url == undefined) {
            item_url.hide();
            toolbar_icons.hide();
            var submenu_html = "";
            submenu_html += '<b>' + 'Menu items' + '</b>';
            submenu_html += '<ul>';
            $.each(item, function (id, menu_item) {
                if (typeof menu_item == 'object') {
                    submenu_html += '<li>';
                    submenu_html += '<label>' + 'Name:' + '  </label><input type="text" name="submenu[' + id + '][name]" value="' + menu_item.name + '"/>  ';
                    submenu_html += '<label>' + 'Link:' + '  </label><input type="text" name="submenu[' + id + '][url]" value="' + menu_item.url + '"/>  ';
                    submenu_html += '<a href="#" class="cancel delete_submenu_item"></a><br style="clear:both"/>';
                    submenu_html += '</li>';
                }
            });
            submenu_html += '</ul>';
            submenu_html += '<div style="clear:both;"></div><a href="#" class="add_submenu_item invite_to_ally" style="float:right;"></a>';
            submenu.html(submenu_html).show();
            submenu.find('ul').sortable();
            $('a.delete_submenu_item').click(function () {
                $(this).parent('li').remove();
            });
            $('a.add_submenu_item').click(function () {
                var id = submenu.find('ul li').length + 1;
                submenu_html = "";
                submenu_html += '<li>';
                submenu_html += '<label>' + 'Name:' + '</label><input type="text" name="submenu[' + id + '][name]" value=""/>  ';
                submenu_html += '<label>' + 'Link:' + '</label><input type="text" name="submenu[' + id + '][url]" value=""/>  ';
                submenu_html += '<a href="#" class="cancel delete_submenu_item"></a><br style="clear:both"/>';
                submenu_html += '</li>';
                submenu.find('ul').append(submenu_html);
                $('a.delete_submenu_item').click(function () {
                    $(this).parent('li').remove();
                });
                submenu.find('ul').sortable();
            });
        } else {
            toolbar_item_url.val(item.url).show();
            item_url.show();
            toolbar_icons.show();
            submenu.empty();
        }
        Quickbar.selectCurrentIcon();
        edit_toolbar_item.fadeIn('fast');
    },
    toggleInfoText: function () {
        $('#quickbar_toggle_text_button').html($('#quickbar_info_text').is(':visible') ? 'Display text' : 'Hide text');
        $('#quickbar_info_text').slideToggle();
    },
    refresh: function (data) {
        var toolbar = $('#toolbar');
        if (data.quickbar) {
            if (toolbar.length) {
                toolbar.remove();
            }
            $('#header').append(data.quickbar);
            Layout.bindResize(true);
            Quickbar.layoutInitializeQuickbar();
        } else {
            toolbar.remove();
        }
    }
};

var Reports = {
    folder_id: null,
    markAll: function (status) {
        $("div.reports_date INPUT[type='checkbox']").attr('checked', status);
    },
    markAllResourceTransportReports: function (status) {
        $("td.resource_transport_reports_date INPUT[type='checkbox']").attr('checked', status);
    },
    toggleMenu: function () {
        var folder_menu_messages = $("#folder_menu_reports"),
            folders = folder_menu_messages.find(".hor_scrollbar_cont span.folder"),
            folders_len = folders.length,
            row = 1,
            per_row = Math.ceil(folders_len / 3),
            size = 0,
            sizes = [0, 0, 0],
            col;
        folder_menu_messages.toggle();
        folders.each(function (index, obj) {
            if (index + 1 >= row * per_row && row < 3) {
                sizes[row - 1] = size;
                size = 0;
                row++;
            }
            size += $(this).outerWidth();
            if (index == folders_len - 1) {
                sizes[row - 1] = size;
            }
        });
        $("#folder_menu_reports .hor_scrollbar_cont").width(Math.max(sizes[0], sizes[1], sizes[2]));
        $("#report_list").toggleClass('with_menu');
    },
    editFolder: function (folder_id) {
        Reports.folder_id = folder_id;
        var params = {
            folder_id: folder_id
        };
        $('#report_folder #folder_name_' + folder_id).css('display', 'block');
        $('#report_folder #save_folder_name_' + folder_id).css('display', 'block');
        $('#report_folder #folder_link_' + folder_id).css('display', 'none');
        Layout.ajaxGet('report', 'getFolder', params, true, function (data) {
            $('#report_folder #folder_name_' + folder_id).val(data.folder.name);
        });
        return false;
    },
    saveFolder: function () {
        var params = {
            folder_id: Reports.folder_id,
            name: $('#report_folder #folder_name_' + Reports.folder_id).val()
        };
        Layout.wnd.getOpenFirst(Layout.wnd.TYPE_REPORT).requestContentPost('report', 'saveFolder', params);
    },
    newFolder: function () {
        var params = {
            folder_id: Reports.folder_id,
            name: $('#report_folder #new_folder_name').val()
        };
        Layout.wnd.getOpenFirst(Layout.wnd.TYPE_REPORT).requestContentPost('report', 'saveFolder', params);
    },
    delFolder: function (folder_id) {
        var params = {
            folder_id: folder_id
        };
        Layout.wnd.getOpenFirst(Layout.wnd.TYPE_REPORT).requestContentPost('report', 'delFolder', params);
    },
    saveFilter: function () {
        var params = {
            trade: $('#trade_filter').attr('checked') ? true : false,
            animated_combat: $('#animated_combat_filter').attr('checked') ? true : false,
            indicator_trade: $('#indicator_trade_filter').attr('checked') ? true : false
        };
        Layout.wnd.getOpenFirst(Layout.wnd.TYPE_REPORT).requestContentPost('report', 'saveFilter', params);
    },
    publishReportDialog: function (report_id) {
        Layout.wnd.Create(Layout.wnd.TYPE_PUBLISH_REPORT, 'Publish report', report_id);
        return false;
    },
    showPublishReportDialog: function (html, wndhandle) {
        wndhandle.setContent(html);
    },
    publishReport: function () {
        var params = {};
        $('#publish_report_dialog_form input[type="checkbox"]').each(function (idx, elm) {
            params[elm.name] = $(elm).attr('checked') ? true : false;
        });
        $('#publish_report_dialog_form input[type="hidden"]').each(function (idx, elm) {
            params[elm.name] = $(elm).val();
        });
        Layout.ajax('report', 'publish_report', params, false, function (data) {
            Reports.showPublishReportDialog(data.html, w(document.getElementById('publish_report_dialog_form')));
        });
        return false;
    },
    deleteAllReports: function () {
        Layout.showConfirmDialog('Delete all reports', 'Do you want to delete all reports in this folder?', function () {
            Layout.wnd.getOpenFirst(Layout.wnd.TYPE_REPORT).sendMessage('reportDeleteAllOfFolder', 'report_form');
        });
    },
    inviteFriends: function () {
        Kontagent.trackEvent({
            userId: Game.uid,
            eventName: 'invite_friends_from_report',
            subtype1: Game.market_id,
            subtype2: Game.world_id,
            subtype3: 'invitation'
        }, function () {});
        Layout.wnd.Create(Layout.wnd.TYPE_INVITE_FRIENDS, 'Invite friends');
    }
};

$(document).ready(function () {
    ReportTranslation.initialize();
});
var ReportTranslation = {
    dialog_id: 'report_translation_dialog',
    dialog_size: {
        'w': 520,
        'h': 404
    },
    initialize: function () {
        $('a.report_translation').click(this.open.bind(this));
    },
    open: function () {
        var div = $('#' + this.dialog_id);
        if (!div.length) {
            div = $('<div id="' + this.dialog_id + '"></div>');
            div.css({
                'position': 'absolute',
                'width': this.dialog_size.w,
                'height': this.dialog_size.h,
                'top': ($('#content').outerHeight() - this.dialog_size.h) / 2,
                'left': ($('#content').outerWidth() - this.dialog_size.w) / 2,
                'zIndex': '10'
            });
            div.html(tmpl('report_translation_dialog_tmpl', {}));
            div.appendTo("#content");
            div.show('slow');
            $('#' + this.dialog_id + ' a.cancel').click(this.close.bind(this));
        }
        Ajax.tmpPost('report_translation', 'form', {}, function (data) {
            $('#' + this.dialog_id + ' .new_window_content_content').html(data.html);
        }.bind(this), {});
    },
    close: function () {
        $('#' + ReportTranslation.dialog_id).remove();
    },
    submit: function () {
        $('#' + ReportTranslation.dialog_id).hide();
        var params = {
            'wrong': $('#report_translation_form textarea[name="reported_translation_wrong"]').val(),
            'suggestion': $('#report_translation_form textarea[name="reported_translation_suggestion"]').val(),
            'comments': $('#report_translation_form textarea[name="reported_translation_comments"]').val(),
            'url_pathname': document.location.pathname,
            'url_search': document.location.search,
            'html': document.documentElement.innerHTML
        };
        $('#' + ReportTranslation.dialog_id).show();
        Ajax.tmpPost('report_translation', 'save', params, function (data) {
            this.close();
        }.bind(this), {});
    }
};

var ReportViewer = {
    dates: [],
    data: {},
    rounds: [],
    round_participants: {
        '1': {
            'att': ['attack_ship', 'bireme', 'trireme', 'small_transporter', 'big_transporter', 'sea_monster'],
            'def': ['attack_ship', 'bireme', 'trireme', 'sea_monster']
        },
        '2': {
            'att': ['attack_ship', 'bireme', 'trireme', 'small_transporter', 'big_transporter', 'demolition_ship', 'colonize_ship'],
            'def': ['demolition_ship']
        },
        '3': {
            'att': ['sword', 'slinger', 'archer', 'hoplite', 'rider', 'chariot', 'catapult', 'minotaur', 'manticore', 'zyklop', 'harpy', 'medusa', 'centaur', 'pegasus', 'cerberus', 'fury'],
            'def': ['sword', 'slinger', 'archer', 'hoplite', 'rider', 'chariot', 'catapult', 'minotaur', 'manticore', 'zyklop', 'harpy', 'medusa', 'centaur', 'pegasus', 'cerberus', 'fury', 'militia']
        }
    },
    elm: {},
    timeout_ids: {},
    current_rounds_idx: null,
    current_round: null,
    current_att_unit: null,
    current_def_unit: null,
    state: {
        'play': false,
        'control_clicked_already': false
    },
    conf: {
        'delay_after_intro': 2000,
        'delay_animate': 2000,
        'delay_next_round': 2000,
        'duration_fade_frame': 1500,
        'duration_fade_units': 1500
    },
    initialize: function (data) {
        var tmp;
        ReportViewer.data = ReportViewer.processData(data);
        ReportViewer.elm.root = $('#report_game_body');
        ReportViewer.elm.report_classic = $('#report_game_body div.report_fight_classic');
        ReportViewer.elm.report_modern = $('#report_game_body div.report_fight_modern');
        ReportViewer.elm.att_unit_list = $('#report_game_body div.report_att ul');
        ReportViewer.elm.def_unit_list = $('#report_game_body div.report_def ul');
        ReportViewer.elm.frames = $('#report_game_body div.report_screen ul li');
        tmp = $('#report_game_body div.report_controls ul li');
        ReportViewer.elm.control = {
            rewind: tmp.find('a.report_controls_rewind'),
            playpause: tmp.find('a.report_controls_playpause'),
            skip: tmp.find('a.report_controls_skip')
        };
        ReportViewer.elm.control.rewind.unbind().bind('click', ReportViewer.controlRewindOnClick);
        ReportViewer.elm.control.playpause.unbind().bind('click', ReportViewer.controlPlayPauseOnClick);
        ReportViewer.elm.control.skip.unbind().bind('click', ReportViewer.controlSkipOnClick);
        ReportViewer.showIntro();
    },
    processData: function (data) {
        var i;
        for (i in data.result.att_units) {
            ReportViewer.rounds.push(i);
        }
        return data;
    },
    controlRewindOnClick: function () {
        if (ReportViewer.state.control_clicked_already) {
            return;
        }
        ReportViewer.state.control_clicked_already = true;
        ReportViewer.reset();
        ReportViewer.showIntro();
        ReportViewer.state.control_clicked_already = false;
    },
    controlPlayPauseOnClick: function () {
        ReportViewer.state.play = !ReportViewer.state.play;
        if (ReportViewer.state.play) {
            ReportViewer.elm.control.playpause.removeClass('report_controls_pause');
            ReportViewer.elm.control.playpause.addClass('report_controls_play');
        } else {
            ReportViewer.elm.control.playpause.removeClass('report_controls_play');
            ReportViewer.elm.control.playpause.addClass('report_controls_pause');
        }
    },
    controlSkipOnClick: function () {
        ReportViewer.reset();
        ReportViewer.elm.report_modern.hide();
        ReportViewer.elm.report_classic.show();
    },
    reset: function () {
        for (var i in ReportViewer.timeout_ids) {
            window.clearTimeout(ReportViewer.timeout_ids[i]);
        }
        $(ReportViewer.elm.frames).stop(true);
        ReportViewer.elm.att_unit_list.empty().hide();
        ReportViewer.elm.def_unit_list.empty().hide();
        ReportViewer.current_rounds_idx = null;
    },
    showIntro: function () {
        ReportViewer.state.play = true;
        if (ReportViewer.hasNavalRounds()) {
            ReportViewer.showFrame(undefined, 0);
            ReportViewer.timeout_ids.showNextRound = window.setTimeout(ReportViewer.showNextRound, ReportViewer.conf.delay_after_intro);
        } else {
            ReportViewer.timeout_ids.showNextRound = window.setTimeout(ReportViewer.showNextRound, ReportViewer.conf.delay_after_intro);
        }
    },
    showOutro: function () {
        var end_frame = ReportViewer.data.player_is_winner ? 6 : 7;
        ReportViewer.showFrame(undefined, end_frame);
        ReportViewer.timeout_ids.controlSkipOnClick = window.setTimeout(ReportViewer.controlSkipOnClick, 4000);
    },
    showNextRound: function () {
        if (null === ReportViewer.current_rounds_idx) {
            ReportViewer.current_rounds_idx = -1;
        } else if (ReportViewer.current_rounds_idx >= ReportViewer.rounds.length - 1) {
            ReportViewer.showOutro();
            return false;
        }
        ReportViewer.current_round = ReportViewer.rounds[++ReportViewer.current_rounds_idx];
        ReportViewer.showRound(ReportViewer.current_round);
    },
    showRound: function (round) {
        if ((typeof ReportViewer.data.result.att_units[round] == 'undefined' || 0 === ReportViewer.data.result.att_units[round].lost.length) && (typeof ReportViewer.data.result.def_units[round] == 'undefined' || 0 === ReportViewer.data.result.def_units[round].lost.length)) {
            ReportViewer.showNextRound();
            return;
        }
        ReportViewer.showFrame(round);
        ReportViewer.build(round);
        ReportViewer.timeout_ids.animate = window.setTimeout(ReportViewer.animate, ReportViewer.conf.delay_animate);
    },
    animate: function () {
        ReportViewer.elm.att_unit_listitems = ReportViewer.elm.att_unit_list.children();
        ReportViewer.elm.def_unit_listitems = ReportViewer.elm.def_unit_list.children();
        var tmp = [];
        ReportViewer.elm.att_unit_listitems.each(function (i) {
            tmp.push(i);
        });
        ReportViewer.elm.att_unit_list_idx = ReportViewer.shuffle(tmp);
        tmp = [];
        ReportViewer.elm.def_unit_list.children().each(function (i) {
            tmp.push(i);
        });
        ReportViewer.elm.def_unit_list_idx = ReportViewer.shuffle(tmp);
        ReportViewer.animateAtt();
        ReportViewer.timeout_ids.animateDef = window.setTimeout(ReportViewer.animateDef, 2000);
    },
    checkReadyToShowNextRound: function () {
        if (ReportViewer.animation_att_units_done && ReportViewer.animation_def_units_done) {
            ReportViewer.animation_att_units_done = false;
            ReportViewer.animation_def_units_done = false;
            ReportViewer.timeout_ids.showNextRound = window.setTimeout(ReportViewer.showNextRound, ReportViewer.conf.delay_next_round);
        }
    },
    build: function (round) {
        var att_lost_units = ReportViewer.data.result.att_units[round].lost,
            att_had_units = ReportViewer.data.result.att_units[round].had,
            att_ul_obj = ReportViewer.elm.att_unit_list,
            att_participating_units;
        var show_defender_units = ((round == 1 || round == 2) && ReportViewer.data.show_defender_units.naval) || (round == 3) && ReportViewer.data.show_defender_units.ground;
        var def_lost_units = (show_defender_units) ? ReportViewer.data.result.def_units[round].lost : [],
            def_had_units = (show_defender_units) ? ReportViewer.data.result.def_units[round].had : [],
            def_ul_obj = ReportViewer.elm.def_unit_list,
            def_participating_units;
        if (round > 3) {
            att_participating_units = ReportViewer.round_participants[3]['att'];
            def_participating_units = ReportViewer.round_participants[3]['def'];
        } else {
            att_participating_units = ReportViewer.round_participants[round]['att'];
            def_participating_units = ReportViewer.round_participants[round]['def'];
        }
        ReportViewer.elm.att_unit_list.empty().hide();
        ReportViewer.elm.def_unit_list.empty().hide();
        ReportViewer.buildUnits(att_participating_units, att_had_units, att_lost_units, att_ul_obj, false);
        ReportViewer.buildUnits(def_participating_units, def_had_units, def_lost_units, def_ul_obj, true);
        ReportViewer.elm.att_unit_list.fadeIn(ReportViewer.conf.duration_fade_units);
        ReportViewer.elm.def_unit_list.fadeIn(ReportViewer.conf.duration_fade_units);
    },
    buildUnits: function (participating_units, had_units, lost_units, ul_obj, are_defender_units) {
        var tpl = '',
            code = '';
        tpl += '<li class="unit_###i###">';
        tpl += '    <span class="report_unit_black small bold">###had###</span>';
        tpl += '    <span class="report_unit_white small bold">###had###</span>';
        tpl += '    <span class="unit_loss_bloodcover" style="display:none"></span>';
        tpl += '    <span class="unit_loss bold">###lost###</span>';
        tpl += '</li>';
        if (are_defender_units && !ReportViewer.data.show_defender_units.ground) {
            code = tpl.replace(/###i###/g, 'unknown').replace(/###\w*###/g, '?');
            ul_obj.append(code);
        } else {
            for (var i in had_units) {
                if (jQuery.inArray(i, participating_units) >= 0) {
                    var lost = '-' + lost_units[i],
                        had = had_units[i];
                    code = tpl.replace(/###i###/g, i).replace(/###had###/g, had).replace(/###lost###/g, lost);
                    ul_obj.append(code);
                }
            }
        }
    },
    showFrame: function (round, frame_idx) {
        if (undefined == frame_idx) {
            frame_idx = ReportViewer.getFrameIdxForRound(round);
        }
        $(ReportViewer.elm.frames).fadeOut();
        $(ReportViewer.elm.frames[frame_idx]).show();
        $(ReportViewer.elm.frames[frame_idx]).css({
            'opacity': 0
        });
        $(ReportViewer.elm.frames[frame_idx]).animate({
            'opacity': 1
        }, ReportViewer.conf.duration_fade_frame);
    },
    getFrameIdxForRound: function (round) {
        var frame_idx = 0;
        if (0 == round) {
            frame_idx = 0;
        } else if (round <= 3) {
            frame_idx = round;
        }
        return frame_idx;
    },
    isLastRound: function (round) {
        return round == ReportViewer.rounds[ReportViewer.rounds.length - 1];
    },
    hasNavalRounds: function () {
        return undefined != ReportViewer.rounds[1];
    },
    shuffle: function (arr) {
        var i = arr.length;
        if (i == 0) {
            return false;
        }
        while (--i) {
            var j = Math.floor(Math.random() * (i + 1)),
                tmp_i = arr[i],
                tmp_j = arr[j];
            arr[i] = tmp_j;
            arr[j] = tmp_i;
        }
        return arr;
    },
    animateAtt: function () {
        if (!ReportViewer.state.play) {
            ReportViewer.timeout_ids.animateAtt = window.setTimeout(ReportViewer.animateAtt, 500);
            return;
        } else {
            window.clearTimeout(ReportViewer.timeout_ids.animateAtt);
        }
        var list = ReportViewer.elm.att_unit_list_idx;
        var idx = list.length ? list.pop() : null;
        if (idx === null) {
            ReportViewer.animation_att_units_done = true;
            ReportViewer.checkReadyToShowNextRound();
            return;
        }
        var delay = Math.round(Math.random * 500);
        ReportViewer.animateUnit(ReportViewer.elm.att_unit_listitems[idx], ReportViewer.animateAtt, delay);
    },
    animateDef: function () {
        if (!ReportViewer.state.play) {
            ReportViewer.timeout_ids.animateDef = window.setTimeout(ReportViewer.animateDef, 500);
            return;
        } else {
            window.clearTimeout(ReportViewer.timeout_ids.animateDef);
        }
        var list = ReportViewer.elm.def_unit_list_idx;
        var idx = list.length ? list.pop() : null;
        if (idx === null) {
            ReportViewer.animation_def_units_done = true;
            ReportViewer.checkReadyToShowNextRound();
            return;
        }
        var delay = Math.round(Math.random * 500);
        ReportViewer.animateUnit(ReportViewer.elm.def_unit_listitems[idx], ReportViewer.animateDef, delay);
    },
    animateUnit: function (unit_elm, callback, delay) {
        unit_elm = $(unit_elm);
        delay = (null === delay) ? 0 : delay;
        callback = (null === callback) ?
        function () {} : callback;
        var loss_elm = unit_elm.children('span.unit_loss'),
            had_elm = unit_elm.children('span.report_unit_white'),
            had_elm2 = unit_elm.children('span.report_unit_black'),
            amount_survived = parseInt(had_elm.html()) - Math.abs(parseInt(loss_elm.html()));
        if (isNaN(amount_survived)) {
            callback();
            return;
        }
        had_elm.html(amount_survived);
        had_elm2.html(amount_survived);
        $(unit_elm).find('span.unit_loss_bloodcover').show().fadeOut(2000);
        if (0 == amount_survived) {
            $(unit_elm).animate({
                'foo': 'bar'
            }, {
                'duration': 1000
            }).animate({
                'opacity': .3
            }, {
                'duration': 500
            });
        }
        loss_elm.show().animate({
            'fontSize': '26px',
            'marginBottom': '-100px',
            'opacity': '0',
            'color': '#FF0000'
        }, {
            'duration': 1000,
            'complete': function () {}
        }).animate({
            'foo': 'bar'
        }, {
            'complete': callback,
            'duration': 100
        });
    },
    insertRemainingUnitsToSimulator: function (att, def, id) {
        var units = {};
        if (id) {
            ReportViewer.initialize(ReportViewer.dates[id]);
        }
        if (def) {
            units.def = ReportViewer.data.remaining_units_def;
        }
        if (att) {
            units.att = ReportViewer.data.remaining_units_att;
        }
        Layout.buildingWindow.open('place', true, 'simulator', units);
    },
    insertAllUnitsToSimulator: function (att, def, id) {
        var units = {};
        if (id) {
            ReportViewer.initialize(ReportViewer.dates[id]);
        }
        if (def) {
            var defUnitData = ReportViewer.data.result.def_units;
            units.def = {};
            for (var round in defUnitData) {
                if ($.inArray('' + round, ReportViewer.rounds) != -1) {
                    for (var unit in defUnitData[round].had) {
                        units.def[unit] = defUnitData[round].had[unit];
                    }
                }
            }
        }
        if (att) {
            var attUnitData = ReportViewer.data.result.att_units;
            units.att = {};
            for (var round in attUnitData) {
                if ($.inArray('' + round, ReportViewer.rounds) != -1) {
                    for (unit in attUnitData[round].had) {
                        units.att[unit] = attUnitData[round].had[unit];
                    }
                }
            }
        }
        Layout.buildingWindow.open('place', true, 'simulator', units);
    }
}

var Tabs = function (id) {
        function resizeTab(ele) {
            if (!ele) {
                return;
            }
            var offset = 26,
                tab_height = parseInt(ele.find('ul.game_tab_list').outerHeight(), 10),
                container = ele.find('div.ui-tabs-panel:visible').children().first(),
                i = null;
            for (i in {
                'maxHeight': '',
                'height': ''
            }) {
                if ((this.foo = container.css(i))) {
                    var new_height = 'auto';
                    if (!isNaN(this.foo = parseInt(this.foo, 10))) {
                        new_height = Math.abs(this.foo - (tab_height - offset));
                    }
                    container.css({
                        i: new_height
                    });
                    return;
                }
            }
        }
        $('#' + id).tabs({
            spinner: '',
            ajaxOptions: {
                success: function () {},
                error: function () {}
            },
            cache: false,
            show: function () {
                resizeTab($(this));
                if (jQuery.browser.msie) {
                    $(this).parents('td').hide().show();
                }
            },
            load: function () {
                resizeTab($(this));
                if (jQuery.browser.msie) {
                    $(this).parents('td').hide().show();
                }
            }
        });
    };

function ContextMenu(opt) {
    var options = opt,
        texts = {
            'goToTown': 'City overview',
            'attack': 'Attack',
            'support': 'Support',
            'trading': 'Trade',
            'god': 'Spells',
            'info': 'Info',
            'claim_info': 'Demand',
            'pillage_info': 'Loot',
            'espionage': 'Espionage',
            'jump_to': 'Go to ',
            'units_info': 'Units',
            'island_info': 'Island info',
            'jump_to_island': 'Go to ',
            'invite_a_friend': 'Invite',
            'colonize': 'Colonize'
        },
        that = this;

    function townType(town) {
        var active_town_id = Game.townId;
        if (ITowns.getTown(town.id) == undefined) {
            return 'town';
        } else if (active_town_id == town.id) {
            return 'current_town';
        } else {
            return 'own_town';
        }
    }

    function getHTML(action) {
        var UIopt = {
            'action': action
        };
        if (opt.type === 'farm_town') {
            var title_postfix = '<span class="farm_town_title_postfix">' + options.town.name + '</span>';
            var title_prefix;
            switch (action) {
            case 'info':
                title_prefix = 'Expansion of';
                break;
            case 'attack':
                title_prefix = 'Attack on';
                break;
            case 'trading':
                title_prefix = 'Trade with';
                break;
            case 'claim_info':
                title_prefix = 'Demand from';
                break;
            case 'pillage_info':
                title_prefix = 'Looting from ';
                break;
            case 'units_info':
                title_prefix = 'Demand units from';
                break;
            }
            Layout.wnd.Create(Layout.wnd.TYPE_FARM_TOWN, title_prefix + ' ' + title_postfix, UIopt, options.town.id);
        } else if (opt.type === 'wonder') {
            switch (action) {
            case 'info':
                Layout.wnd.Create(Layout.wnd.TYPE_WONDERS, options.town.name, {}, options.town.ix, options.town.iy);
                break;
            case 'jump_to':
                WMap.jumpToPosByCoords(options.town.ix, options.town.iy);
                break;
            }
        } else if (opt.type === 'island') {
            switch (action) {
            case 'island_info':
                Layout.wnd.Create(Layout.wnd.TYPE_ISLAND, '', {}, opt.town);
                break;
            case 'jump_to_island':
                WMap.jumpToPosByCoords(opt.town.ix, opt.town.iy);
                break;
            }
        } else if (opt.type === 'invite_to_colo_flag') {
            switch (action) {
            case 'colonize':
                Layout.wnd.Create(Layout.wnd.TYPE_UNINHABITED_PLACE, 'Found a new city', {}, {
                    'target_x': opt.town.ix,
                    'target_y': opt.town.iy,
                    'target_number_on_island': opt.town.nr
                });
                break;
            case 'invite_a_friend':
                WndHandlerInviteFriends.selectSpotOnMap(opt.town);
                break;
            }
        } else {
            var w;
            var id = {
                id: options.town.id
            };
            var GameTown = ITowns.getTown(options.town.id);
            if (options.type == 'own_town' || options.type == 'current_town') {
                if (GameTown.has_conqueror && !(action == 'attack' || action == 'support')) {
                    Layout.conquestWindow.open(options.town.id);
                    return;
                }
            }
            switch (action) {
            case 'goToTown':
                Layout.townSwitch(options.town.id, options.town.name);
                Layout.townIndex.open(options.town.id, options.town.name);
                break;
            case 'attack':
                Layout.wnd.Create(Layout.wnd.TYPE_TOWN, options.town.name, UIopt, id);
                break;
            case 'support':
                Layout.wnd.Create(Layout.wnd.TYPE_TOWN, options.town.name, UIopt, id);
                break;
            case 'jump_to':
                var x = options.town.ix == undefined ? options.town.x : options.town.ix;
                var y = options.town.iy == undefined ? options.town.y : options.town.iy;
                WMap.jumpToPosByCoords(x, y);
                WMap.mapTiles.focusTown(options.town.id);
                break;
            case 'info':
                w = Layout.wnd.Create(Layout.wnd.TYPE_TOWN, options.town.name, UIopt, id);
                w = undefined;
                break;
            case 'trading':
                w = Layout.wnd.Create(Layout.wnd.TYPE_TOWN, options.town.name, UIopt, id);
                w = undefined;
                break;
            case 'espionage':
                w = Layout.wnd.Create(Layout.wnd.TYPE_TOWN, options.town.name, UIopt, id);
                w.call('getPage', action, options.town.id);
                w = undefined;
                break;
            case 'god':
                w = Layout.wnd.Create(Layout.wnd.TYPE_TOWN, options.town.name, UIopt, id);
                w = undefined;
                break;
            default:
                w = Layout.wnd.Create(Layout.wnd.TYPE_TOWN, options.town.name, options.town.name, UIopt, id);
                w.call('getPage', action, options.town.id);
                w = undefined;
                break;
            }
        }
    }

    function animateMenuIcons(menu, pos, r, timeout) {
        var i;
        if (r > 60) {
            return;
        }
        for (i in pos) {
            menu[i].style.left = ~~ (r * pos[i].x) + 'px';
            menu[i].style.top = ~~ (r * pos[i].y) + 'px';
        }
        window.setTimeout(function () {
            animateMenuIcons(menu, pos, r + 15, timeout);
        }, timeout);
    }

    function arrangeMenuIcons(names) {
        var menu = document.createElement('div');
        var n = 1,
            r = 0,
            i, cos = Math.cos,
            sin = Math.sin,
            pi = Math.PI,
            pos = {},
            list = {};
        if (names[0]) {
            createHTML(names.shift(), true);
        }
        for (i = 0; i < names.length; i++) {
            createHTML(names[i]);
            pos[names[i]] = {
                x: cos((i) / names.length * 2 * pi),
                y: sin((i) / names.length * 2 * pi)
            };
        }

        function createHTML(name, front) {
            var div = document.createElement('div'),
                span = document.createElement('span');
            span.className = 'text';
            span.innerHTML = texts[name] + '<span class="bottom"></span>';
            div.appendChild(span);
            div.id = name;
            div.className = 'context_icon';
            if (front) {
                div.style.zIndex = '5';
            }
            $(div).bind('mousedown', function () {
                getHTML(this.id);
            });
            list[name] = div;
            menu.appendChild(div);
        }
        menu.id = 'context_menu';
        animateMenuIcons(list, pos, r, 20);
        return menu;
    }
    if (opt.type == 'determine') {
        if (!opt.town.tp || opt.town.tp == 'town') {
            opt.type = townType(options.town);
        } else {
            opt.type = opt.town.tp;
        }
    }
    switch (opt.type) {
    case 'farm_town':
        if (opt.town.relation_status == 1) {
            return arrangeMenuIcons(['claim_info', 'pillage_info', 'units_info', 'trading', 'info']);
        } else {
            return arrangeMenuIcons(['attack']);
        }
    case 'town':
        return arrangeMenuIcons(['info', 'attack', 'support', 'trading', 'espionage', 'god', 'jump_to']);
    case 'own_town':
        return arrangeMenuIcons(['goToTown', 'attack', 'support', 'trading', 'god', 'info', 'jump_to']);
    case 'current_town':
        return arrangeMenuIcons(['goToTown', 'god', 'info', 'jump_to']);
    case 'ghost_town':
        return arrangeMenuIcons(['info', 'attack', 'support', 'espionage', 'jump_to']);
    case 'wonder':
        return arrangeMenuIcons(['info', 'jump_to']);
    case 'island':
        return arrangeMenuIcons(['island_info', 'jump_to_island']);
    case 'invite_to_colo_flag':
        return arrangeMenuIcons(['invite_a_friend', 'colonize']);
    default:
        return false;
    }
}

var UnitOrder = {
    unit_id: '',
    slider: null,
    units: null,
    orders: null,
    barracks: false,
    firstOrderCompletedAt: null,
    unitTimeoutHandle: null,
    imageCountdown: null,
    selected_unit_id: null,
    finish_for_gold_dialog_text: null,
    text_finish_for_gold_popup: null,
    wnd: null,
    availableGold: 0,
    finishGoldOrderCost: 0,
    finish_for_gold_enabled: false,
    order_queue_template: null,
    init: function (units, orders, barracks, selected_unit_id, finishGoldOrderCost, availableGold, finish_for_gold_enabled) {
        var _self = this;
        this.units = units;
        this.orders = orders;
        this.barracks = barracks;
        this.finishGoldOrderCost = finishGoldOrderCost;
        this.availableGold = availableGold || 0;
        this.finish_for_gold_enabled = finish_for_gold_enabled;
        this.initSlider();
        this.selected_unit_id = selected_unit_id;
        this.selectUnit(selected_unit_id);
        this.initializeOrderQueueTemplate();
        this.updateOrders();
        this.initializePopups();
        $("#unit_order_input").keydown(function (e) {
            if (e.keyCode == 13) {
                _self.build(w($(this).context));
            }
        });
    },
    initializePopups: function () {
        if (this.barracks) {
            this.finish_for_gold_dialog_text = s(ngettext('Are you sure you want to cut the recruitment time in half for %1 gold?', new Array('Are you sure you want to cut the recruitment time in half for %1 gold?', 'Are you sure you want to cut the recruitment time in half for %1 gold?'), this.finishGoldOrderCost), this.finishGoldOrderCost);
        } else {
            this.finish_for_gold_dialog_text = s(ngettext('Are you sure you want to cut the construction time in half for %1 gold?', new Array('Are you sure you want to cut the construction time in half for %1 gold?', 'Are you sure you want to cut the construction time in half for %1 gold?'), this.finishGoldOrderCost), this.finishGoldOrderCost);
        }
        this.text_finish_for_gold_popup = '<span class="bold">';
        if (this.barracks) {
            this.text_finish_for_gold_popup += s(ngettext('You can cut the recruitment time in half for %1 gold.', new Array('You can cut the recruitment time in half for %1 gold.', 'You can cut the recruitment time in half for %1 gold.'), this.finishGoldOrderCost), this.finishGoldOrderCost);
        } else {
            this.text_finish_for_gold_popup += s(ngettext('You can cut the construction time in half for %1 gold.', new Array('You can cut the construction time in half for %1 gold.', 'You can cut the construction time in half for %1 gold.'), this.finishGoldOrderCost), this.finishGoldOrderCost);
        }
        this.text_finish_for_gold_popup += '<br /><br />';
        this.text_finish_for_gold_popup += s(ngettext('Available gold: %1', new Array('Available gold: %1', 'Available gold: %1'), this.availableGold), this.availableGold);
        if (this.orders != null) {
            $('#current_building_order_queue_count').text(this.orders.length);
        }
        $('img.wood').mousePopup(new MousePopup('Wood'));
        $('img.stone').mousePopup(new MousePopup('Rock'));
        $('img.iron').mousePopup(new MousePopup('Silver coins'));
        $('img.favor').mousePopup(new MousePopup('Favor'));
        $('img.population').mousePopup(new MousePopup('Population'));
        $('img.buildtime').mousePopup(new MousePopup(this.barracks ? 'Recruitment time' : 'Construction time'));
        $.each(GameData.units, function (unit) {
            $("#" + unit).setPopup(unit);
        });
        $('a.unit_order_tasks_finish').mousePopup(new MousePopup(this.text_finish_for_gold_popup));
    },
    initializeOrderQueueTemplate: function () {
        if (!this.order_queue_template) {
            if ($("#orders_tmpl").length > 0) {
                this.order_queue_template = $("#orders_tmpl").html().replace(/<!\[CDATA\[/, "").replace(/\]\]>/, "");
            }
        }
    },
    handleEvents: function (event) {
        var unit_id;
        if (event.type == GPEvents.global.town.units_changed) {
            var units = ITowns.getTown(Game.townId).units();
            var unitsOuter = ITowns.getTown(Game.townId).unitsOuter();
            for (unit_id in units) {
                if (!units.hasOwnProperty(unit_id)) {
                    continue;
                }
                $('#unit_order_count_' + unit_id).html(units[unit_id]);
                $('#unit_order_count_shadow_' + unit_id).html(units[unit_id]);
                $('#unit_order_tab_' + unit_id + ' .unit_order_total').html(unitsOuter[unit_id] + units[unit_id]);
            }
        }
    },
    selectUnit: function (unit_id) {
        if (unit_id != null) {
            this.unit_id = unit_id;
            var unit = UnitOrder.units[unit_id];
            this.showUnit(unit);
            $('.unit_active').removeClass('unit_active');
            $('#unit_order_tab_' + unit_id).addClass('unit_active');
        }
    },
    changeCount: function (e) {
        var count = parseInt($('#unit_order_input')[0].value, 10);
        if (isNaN(count)) {
            return;
        }
        var unit = UnitOrder.units[this.unit_id];
        this.showCosts(unit, count);
        $('#unit_order_confirm').css('visibility', count ? '' : 'hidden');
    },
    initSlider: function () {
        var element_slider = $('#unit_order_slider');
        this.slider = new Slider({
            elementMin: $('#unit_order_min'),
            elementMax: $('#unit_order_max'),
            elementDown: $('#unit_order_down'),
            elementUp: $('#unit_order_up'),
            elementInput: $('#unit_order_input'),
            elementSlider: element_slider
        });
        element_slider.bind('change', function () {
            UnitOrder.changeCount(UnitOrder.slider.getValue());
        });
    },
    showUnit: function (unit) {
        $('#unit_order_unit_name').text(unit.name);
        $('#unit_order_unit_hidden')[0].value = unit.id;
        try {
            this.slider.setMax(unit.max_build);
            this.slider.setValue(unit.max_build);
        } catch (e) {}
        var dependencies = $('#unit_order_dependencies');
        if ($(unit.missing_building_dependencies).length || $(unit.missing_research_dependencies).length) {
            dependencies.show();
            var research_text = '';
            var building_text = '';
            if (unit.missing_building_dependencies) {
                jQuery.each(unit.missing_building_dependencies, function (name, level) {
                    building_text += name + ': ' + level + '; ';
                });
            }
            if (unit.missing_research_dependencies.length) {
                research_text = 'Research:' + ' ' + unit.missing_research_dependencies.join(', ');
            }
            $('#unit_order_dependencies').text('Required:' + '\n' + building_text + research_text);
        } else {
            dependencies.hide();
        }
        $('#unit_order_unit_big_image').attr('src', Game.img() + '/game/units/' + unit.id + '_90x90.jpg');
        $('#unit_order_unit_big_image').setPopup(unit.id + '_details');
        $('#unit_order_unit_wood').text(unit.resources.wood);
        $('#unit_order_unit_stone').text(unit.resources.stone);
        $('#unit_order_unit_iron').text(unit.resources.iron);
        $('#unit_order_unit_favor').text(unit.favor);
        $('#unit_order_unit_pop').text(unit.population);
        $('#unit_order_unit_build_time').text(readableSeconds(unit.build_time));
        $('#unit_order_att').attr('class', 'unit_order_att_' + unit.attack_type);
        $('#unit_order_unit_attack').text(unit.attack);
        $('#unit_order_unit_speed').text(unit.speed);
        if (unit.attack_type != undefined) {
            $('#unit_order_att').setPopup('unit_type_' + unit.attack_type);
        }
        $('#unit_order_unit_transport').text(unit.capacity);
        $('#unit_order_unit_defense').text(unit.defense);
        $('#unit_order_unit_booty').text(unit.booty);
        $('#unit_order_unit_def_hack').text(unit.def_hack);
        $('#unit_order_unit_def_pierce').text(unit.def_pierce);
        $('#unit_order_unit_def_distance').text(unit.def_distance);
    },
    showCosts: function (unit, count) {
        var res_id;
        for (res_id in unit.resources) {
            var value = unit.resources[res_id] * count;
            $('#unit_order_all_' + res_id).text(value);
        }
        $('#unit_order_all_pop').text(unit.population * count);
        $('#unit_order_all_favor').text(unit.favor * count);
        $('#unit_order_all_build_time').text(readableSeconds(unit.build_time * count));
    },
    updateCounts: function (units) {
        var i;
        for (i in units) {
            var unit = units[i];
            $('#unit_order_max_build_' + unit.id).html('+' + unit.max_build);
            $('#unit_order_count_' + unit.id).html(unit.count);
            $('#unit_order_count_shadow_' + unit.id).html(unit.count);
        }
    },
    updateOrders: function () {
        if (UnitOrder.order_queue_template) {
            var rendered_template = tmpl(UnitOrder.order_queue_template, {
                orders: UnitOrder.orders,
                barracks: UnitOrder.barracks,
                finish_for_gold_enabled: UnitOrder.finish_for_gold_enabled
            });
            $('#tasks').html(rendered_template);
        }
        var order, i, unit_orders = UnitOrder.orders;
        for (i in unit_orders) {
            if (unit_orders.hasOwnProperty(i)) {
                order = unit_orders[i];
                $('#unit_order_' + i).mousePopup(new MousePopup(s(GameData.units[order.unit_id].name, order.completed_human)));
                $('#unit_order_' + i + ' div.unit_order_task_time').mousePopup(new MousePopup(s('Completion %1', order.completed_human)));
                if (order.refund) {
                    var r = order.refund;
                    var content = s('Reimbursement of wood: %1 rock: %2 silver coins: %3 favor: %4', r.resources.wood, r.resources.stone, r.resources.iron, r.favor);
                    $('#unit_order_' + i + ' a.unit_order_cancel').mousePopup(new MousePopup(content));
                }
            }
        }
        var unit_order_current = $('#unit_order_0 div.unit_order_task_time');
        if (unit_order_current.length > 0 && this.orders[0].units_left > 0) {
            this.startImageCountdown();
            unit_order_current.countdown(this.orders[0].to_be_completed_at, {});
            unit_order_current.bind('finish', function () {
                UnitOrder.finishHandler(w(this));
            });
            var build_time = ~~ ((this.orders[0].to_be_completed_at - this.orders[0].created_at) / this.orders[0].count);
            UnitOrder.finishedHandlerUnit(false, build_time);
        }
        if (this.orders != null) {
            $('#current_building_order_queue_count').text(this.orders.length);
        }
    },
    finishedHandlerUnit: function (self_called, build_time) {
        var unit_order_current;
        var order;
        var end_time_unit;
        if (self_called) {
            unit_order_current = $('#unit_order_0 div.unit_order_task_time');
            if (unit_order_current.length > 0 && this.orders[0].units_left > 0) {
                order = this.orders[0];
                order.units_left--;
                order.seconds_left -= build_time;
                $('#unit_order_0 div.unit_order_task_value').html(this.orders[0].units_left);
                this.redrawCancelRefund(order, $('#unit_order_0 a.unit_order_cancel'));
                if (order.units_left > 0) {
                    this.startImageCountdown();
                } else {
                    return;
                }
            } else {
                if (this.unitTimeoutHandle != null) {
                    window.clearTimeout(this.unitTimeoutHandle);
                }
            }
        } else {
            if (this.unitTimeoutHandle != null) {
                window.clearTimeout(this.unitTimeoutHandle);
            }
        }
        if (this.orders[0]) {
            end_time_unit = this.orders[0].to_be_completed_at - ((this.orders[0].units_left - 1) * build_time) - Timestamp.now();
            this.unitTimeoutHandle = window.setTimeout(function () {
                UnitOrder.finishedHandlerUnit(true, build_time);
            }, end_time_unit * 1000);
        }
    },
    startImageCountdown: function () {
        var unit_order_current;
        var completed_at;
        var units_left;
        var start_time_unit;
        var end_time_units;
        var build_time;
        var unit_order_spendable;
        unit_order_current = $('#unit_order_0 div.unit_order_task_time');
        if (unit_order_current.length > 0 && this.orders[0].units_left > 0) {
            completed_at = this.orders[0].to_be_completed_at;
            units_left = this.orders[0].units_left;
            build_time = Math.round((completed_at - this.orders[0].created_at) / this.orders[0].count);
            start_time_unit = completed_at - (units_left * build_time);
            end_time_units = completed_at - ((units_left - 1) * build_time);
            unit_order_spendable = $('#unit_order_0 div.unit_order_spendable').length > 0;
            this.imageCountdown = new ImageCountdown(unit_order_current, start_time_unit, end_time_units, {
                'width': '50px',
                'height': '50px',
                'top': 22,
                'left': (unit_order_spendable) ? 6 : 21
            }, {
                'width': '50px',
                'height': '3200px'
            });
        }
    },
    finishHandler: function (wnd) {
        wnd.requestContentGet(GameData.buildings[this.barracks ? 'barracks' : 'docks'].controller, 'load', {});
    },
    action: function (action, id, wnd) {
        wnd.requestContentPost(GameData.buildings[this.barracks ? 'barracks' : 'docks'].controller, action, {
            id: id
        });
    },
    build: function (wnd) {
        var unit_id = $('#unit_order_unit_hidden')[0].value,
            amount = parseInt($('#unit_order_input')[0].value, 10),
            params = {
                unit_id: unit_id,
                amount: (isNaN(amount) ? 0 : amount)
            };
        wnd.requestContentPost(GameData.buildings[this.barracks ? 'barracks' : 'docks'].controller, 'build', params, function () {
            Kontagent.trackEvent({
                userId: Game.uid,
                eventName: unit_id,
                level: amount,
                subtype1: Game.market_id,
                subtype2: Game.world_id,
                subtype3: 'unit_order'
            }, function () {});
        });
    },
    toggleUnits: function () {
        var unit_order_show = $('#unit_order_show');
        if (unit_order_show.hasClass('unit_order_hide')) {
            unit_order_show.mousePopup(new MousePopup('<h4>' + 'Only show researched units.' + '</h4>'));
            $('div.unit_tab').fadeOut();
        } else {
            unit_order_show.mousePopup(new MousePopup('<h4>' + 'Show all units.' + '</h4>'));
            $('div.unit_tab').fadeIn();
        }
        unit_order_show.toggleClass('unit_order_hide');
    },
    redrawCancelRefund: function (order, jQTarget) {
        if (order.refund) {
            var r = $.extend(true, {}, GameData.units[order.unit_id]);
            order.refund.favor = Math.floor(r.favor * order.units_left * GameData.unit_order_refund_factor);
            order.refund.resources.wood = Math.floor(r.resources.wood * order.units_left * GameData.unit_order_refund_factor);
            order.refund.resources.stone = Math.floor(r.resources.stone * order.units_left * GameData.unit_order_refund_factor);
            order.refund.resources.iron = Math.floor(r.resources.iron * order.units_left * GameData.unit_order_refund_factor);
            r = order.refund;
            var content = s('Reimbursement of wood: %1 rock: %2 silver coins: %3 favor: %4', Math.floor(r.resources.wood), Math.floor(r.resources.stone), Math.floor(r.resources.iron), Math.floor(r.favor));
            jQTarget.mousePopup(new MousePopup(content));
        }
    },
    confirm_finish_for_gold: function (order_id, wnd, button, unit_type) {
        var disabled = 'disabled',
            confirm, cancel, that = this;
        button = $(button);
        if (button.hasClass(disabled)) {
            return;
        }
        if (wnd) {
            this.wnd = wnd;
        }
        button.addClass(disabled);
        confirm = function () {
            that.finishForGold(order_id, unit_type);
        };
        if (Layout.show_confirmation_popup) {
            cancel = function () {
                button.removeClass(disabled);
            };
            Layout.showConfirmDialog((this.barracks ? 'Reduce the recruitment time by half' : 'Cut the construction time in half'), UnitOrder.finish_for_gold_dialog_text, confirm, null, cancel, null, null, 'Do not show this window again');
        } else {
            confirm();
        }
    },
    finishForGold: function (order_id, unit_type) {
        this.wnd.requestContentPost(GameData.buildings[this.barracks ? 'barracks' : 'docks'].controller, 'finish_for_gold', {
            order_id: order_id
        }, function () {
            Kontagent.trackEvent({
                userId: Game.uid,
                eventName: 'finish_unit_' + unit_type,
                level: 1,
                subtype1: Game.market_id,
                subtype2: Game.world_id,
                subtype3: 'buy_pa_feature'
            }, function () {});
        });
    },
    goldChanged: function (new_gold) {
        UnitOrder.init(UnitOrder.units, UnitOrder.orders, UnitOrder.barracks, UnitOrder.selected_unit_id, UnitOrder.finishGoldOrderCost, new_gold, UnitOrder.finish_for_gold_enabled);
    }
};

function UnitSlider() {
    var container = null;
    var input = null;
    var self = this;
    var button = null;
    var unit_sldr = null;
    this.initialize = function (id, minval, maxval, callback, onclick) {
        maxval = parseInt(maxval, 10);
        minval = parseInt(minval, 10);
        callback = callback ||
        function () {};
        input = $('input#' + id);
        input.click(function () {
            if (onclick) {
                onclick();
            }
        });
        if (input.length < 1) {
            input = $("input[name='" + id + "']");
        }
        button = input.prev();
        container = button.prev();
        container.unbind();
        button.unbind();
        var sldr = container.children('div.unit_slider');
        unit_sldr = new Slider({
            elementInput: input,
            elementSlider: sldr,
            elementDown: sldr.prev(),
            elementUp: sldr.next(),
            min: minval,
            max: maxval,
            max_overwrite: true,
            callback: callback,
            onmousedown: onclick
        });
        button.bind('click', {
            msg: id
        }, function () {
            self.showSlider();
            if (onclick) {
                onclick();
            }
        });
        unit_sldr.bind('slidestop', {
            msg: this
        }, function () {
            input.change();
        });
    };
    this.setValue = function (value) {
        unit_sldr.setValue(value);
    };
    this.setMax = function (max) {
        unit_sldr.setValue(Math.min(max, input.val() || 0));
        unit_sldr.setMax(max);
    };
    this.showSlider = function () {
        this.hideAllSliders();
        var input = container.next(input);
        var parent = input.parent();
        if (container.is(':visible') || container.css('display') != 'none') {
            this.hideAllSliders();
        } else {
            parent.addClass('active');
            container.fadeIn();
        }
        container.bind('mouseover', function () {
            $(this).focus();
        });
        container.bind('mouseleave', function () {
            self.hideAllSliders();
            $(this).unbind('mouseover mouseleave');
        });
    };
    this.hideAllSliders = function () {
        input.change();
        var unit_containers = $('div.unit_container');
        $('.active div.unit_slider_container').fadeOut('fast');
        unit_containers.each(function () {
            $(this).removeClass('active');
        });
        setTimeout(function () {
            $('div.unit_slider_container').each(function () {
                if (!$(this).parent().is('.active') && $(this).attr('style') != null) {
                    $(this).removeAttr('style');
                }
            });
        }, 500);
    };
}

var Worldmap = {
    size_x: 150,
    size_y: 150,
    marker_image_size: 25,
    position_marker: null,
    position_marker_size: {},
    position_tile_offset: {},
    initialize: function (position, position_tile_offset) {
        var worldmap = $('#worldmap');
        this.position_tile_offset = position_tile_offset;
        worldmap.unselectable();
        worldmap.click(function (e) {
            Worldmap.handlerDown(e);
        });
        this.setPosition(position);
        this.position_marker = $('#worldmap_position_marker');
        var canvas_width = WMap.xSize - $('#minimap_info_bar').width();
        var canvas_height = WMap.ySize;
        var chunks = Math.round(MapTiles.mapSize / Minimap.map_tiles_per_minimap_tile);
        var map_size_px = chunks * 400;
        size_x = Math.round((canvas_width * this.size_x) / (map_size_px / 10));
        size_y = Math.round(canvas_height * this.size_y / (map_size_px / 10));
        this.position_marker.css('width', size_x + 'px');
        this.position_marker.css('height', size_y + 'px');
        this.position_marker_size = {
            x: size_x,
            y: size_y
        };
    },
    setPosition: function (coord) {
        if (!coord || !this.position_marker || coord.x < 0 || coord.y < 0 || coord.x > MapTiles.mapSize || coord.y > MapTiles.mapSize || isNaN(coord.y) || isNaN(coord.y)) {
            return;
        }
        coord.x = coord.x + this.position_tile_offset.x;
        coord.y = coord.y + this.position_tile_offset.y;
        var sea = WMap.getSea(coord.x, coord.y);
        var sea_coord = WMap.getCoordsFromSea(sea);
        $('#worldmap').attr("style", "background: url(" + Game.img(false) + "/game/minimap/sea_maps/sea_" + sea + ".png) #123D70");
        pos_x = Math.round((this.size_x / MapTiles.mapSize) * (coord.x - sea_coord.x - this.position_tile_offset.x) * 10);
        pos_y = Math.round((this.size_y / MapTiles.mapSize) * (coord.y - sea_coord.y - this.position_tile_offset.y) * 10);
        this.position_marker.css('left', pos_x + "px");
        this.position_marker.css('top', pos_y + "px");
        $('#position_input_x').val(Math.round(coord.x));
        $('#position_input_y').val(Math.round(coord.y));
        $('#position_input_sea').val(sea);
    },
    handlerDown: function (event) {
        var coord = this.getMapCoordFromEvent(event);
        Minimap.goto(coord.x - this.position_tile_offset.x, coord.y - this.position_tile_offset.y);
    },
    getMapCoordFromEvent: function (event) {
        var canvas_position = Minimap.getAbsolutePosition($('#worldmap')[0]);
        var pos_x = event.offsetX ? (event.offsetX) : event.pageX - canvas_position.x - this.position_marker_size.x * 0.5;
        var pos_y = event.offsetY ? (event.offsetY) : event.pageY - canvas_position.y - this.position_marker_size.y * 0.5;
        var sea_coords = WMap.getCoordsFromSea($('#worldmap').css("background").match(/sea_([0-9]{1,2})\.png/)[1]);
        var coord_x = Math.round((pos_x / this.size_x) * MapTiles.mapSize) + sea_coords.x;
        var coord_y = Math.round((pos_y / this.size_y) * MapTiles.mapSize) + sea_coords.y;
        return {
            x: coord_x,
            y: coord_y
        };
    },
    scrollWorldMap: function (delta_x, delta_y) {
        var coord = Minimap.position;
        coord.x += (MapTiles.mapSize * delta_x / 10);
        coord.y += (MapTiles.mapSize * delta_y / 10);
        Minimap.goto(coord.x, coord.y);
    },
    jumpToMinimap: function () {
        var coord_x = parseInt($('#position_input_x').val());
        var coord_y = parseInt($('#position_input_y').val());
        Minimap.jumpTo(coord_x, coord_y);
    }
};

function GPGameLoader(_dates) {
    var that = this;
    var outstanding = 0;
    var loader = null;
    var template;
    var elm = {
        'bar': $('#load_progress_bar')
    };
    var remotedates = _dates;
    var finishloadhooks = [];
    var mapTemplate = '';
    var verbose = false;
    this.addFinishLoadedHook = function (fn) {
        finishloadhooks.push(fn);
    };

    function everythingLoaded() {
        var i = finishloadhooks.length;
        if (verbose) {
            debug("GameLoader: call finishloading hooks");
        }
        while (i--) {
            finishloadhooks[i]();
        }
        var wmap = $('#content').clone();
        wmap.empty().attr('id', 'wmap').css({
            position: 'absolute',
            top: 0,
            left: 0,
            width: '100%',
            height: '100%'
        }).appendTo('#main_area').html(mapTemplate);
        map_init();
        $('#content').remove();
        template.remove();
        mapTemplate = null;
    }

    function loadSuccess(item) {
        $('#loader_step_' + item).addClass('loading_done');
        if (--outstanding === 0) {
            window.setTimeout(everythingLoaded, 2);
        }
        elm.bar.css('width', ((1 - outstanding / 5) * 100).toString() + '%');
    }

    function localRecvCallback(ok, data, item) {
        var obj;
        if (!ok) {
            if (verbose) {
                debug("GameLoader: local Recv Callback (data) - fail [" + item + "], requesting remote");
            }
            requestRemote(item);
        } else {
            if (verbose) {
                debug("GameLoader: lcoal recv Callback (data) - ok [" + item + "]");
            }
            obj = {};
            obj[item] = data;
            GameData.add(obj);
            loadSuccess(item);
        }
    }

    function localCheckIntegryCallback(ok, data, item) {
        if (!ok || data != remotedates[item]) {
            if (verbose) {
                debug("GameLoader: local integry check on [" + item + "] failed - requesting remote");
            }
            requestRemote(item);
        } else {
            if (verbose) {
                debug("GameLoader local integry check on [" + item + "] ok");
            }
            LocalStore.gget(item, localRecvCallback, item, that);
        }
    }

    function recvRemoteCallback(data, item) {
        if (verbose) {
            debug("GameLoader: remote recv of [" + item + "]");
        }
        if (remotedates[item] !== undefined) {
            if (LocalStore.check()) {
                if (verbose) {
                    debug("... adding to local storage");
                }
                LocalStore.gset(item + '_mtime', data.mtime);
                LocalStore.gset(item, $.parseJSON(data.data));
            }
        }
        switch (item) {
        case 'map':
            mapTemplate = data.data;
            break;
        case 'bar':
            TempBarData = data.data;
            break;
        default:
            var obj = {};
            obj[item] = $.parseJSON(data.data);
            GameData.add(obj);
            break;
        }
        loadSuccess(item);
    }

    function requestRemote(_type) {
        loader.tmpGet('data', 'get', {
            type: _type
        }, false, function (scpe, data, flags) {
            recvRemoteCallback(data, _type);
        });
    }

    function requestLocal(_type) {
        LocalStore.gget(_type + '_mtime', localCheckIntegryCallback, _type, that);
    }

    function unsupportedBrowserCheck() {
        var browser = $.browser;
        if (browser.msie && browser.version) {
            var ver = browser.version.split('.');
            if (ver[0] < 7) {
                jQuery.blocker({
                    'html': $("<div><div style=\"text-align: center;\"><b>" + 'Browser not supported' + "</b></div>" + "<div style=\"margin-top: 5px;\">" + 'You\'re using a browser that we no longer support.' + '<br>' + 'This can result in a diminished game functionality or errors.' + '<br>' + 'We recommend that you update your browser to guarantee the best possible playability!' + '<br>' + s('You can find alternative browsers at %1', '<a href="http://www.browserchoice.eu" target="_blank">www.browserchoice.eu</a>') + "</div><br></div>").append(button('Continue at your own risk', {
                        style: "float: right;",
                        onClick: "jQuery.blocker.unblock();",
                        href: "#"
                    })),
                    'caching': false,
                    'gameloader': true,
                    'bgClickable': false,
                    'height': 250,
                    'callback': function () {
                        init();
                    }
                });
                return;
            }
        }
        init();
    }

    function init() {
        var item;
        if (verbose) {
            debug("GameLoader - Init!");
        }
        template = $('#loader').appendTo('body');
        if (verbose) {
            if (LocalStore.check()) {
                debug("GameLoader: local Storage (persist) is available (Type: " + LocalStore.getType() + ")");
            }
        }
        loader = new gpAjax(null, false);
        if (!loader) {
            if (verbose) {
                debug("cannot initialize gpajax loader.");
            }
            throw "Cannot initialize gpajax loader for gameloader!";
        }
        if (window.navigator.userAgent == '') {
            var params = {};
            params.appCodeName = window.navigator.appCodeName;
            params.appName = window.navigator.appName;
            params.version = window.navigator.appVersion;
            loader.tmpPost('player', 'log_browser', params, false, function () {});
        }
        var ItemList = {};

        function addOutstanding(type) {
            ItemList[type] = true;
            outstanding++;
        }
        addOutstanding('buildings');
        addOutstanding('powers');
        addOutstanding('units');
        addOutstanding('map');
        addOutstanding('bar');
        for (item in ItemList) {
            if (remotedates[item] !== undefined) {
                if (LocalStore.check()) {
                    requestLocal(item);
                } else {
                    requestRemote(item);
                }
            } else {
                requestRemote(item);
            }
        }
    }
    unsupportedBrowserCheck();
}

var ABTests = {
    'addGoldToResBar': function () {
        var abPlugin = (function () {
            var pop = new MousePopup('');
            var gold = $('#gold');
            var ev = GPEvents.global;
            var reload = true;
            var time = Timestamp.now();
            this.init = function () {
                ev.bind(ev.spend_gold, function () {
                    reload = true;
                });
                gold.bind({
                    'click': function () {
                        Layout.wnd.Create(Layout.wnd.TYPE_PREMIUM, 'Premium', {});
                    },
                    'mouseenter': function () {
                        if (reload || time + 6E2 < Timestamp.now()) {
                            updateTooltip();
                        }
                    }
                }).mousePopup(pop);
            };

            function updateTooltip() {
                Layout.ajax('premium_features', 'current_gold', {}, false, function (data) {
                    value = data.gold;
                    pop.initialize(getStr(data.gold));
                    pop.showDiv();
                    reload = false;
                    time = Timestamp.now();
                });
            }

            function getStr(gold_value) {
                return '<h4>' + 'Gold' + '</h4>' + '<span>' + s('Available gold: %1', gold_value) + '</span>';
            }
            this.init();
        }());
        return abPlugin;
    },
    'getKontagentAbcEventPostfix': function (abc_group) {
        var postfix = "";
        if (abc_group == 'A' || abc_group == 'B' || abc_group == 'C') {
            postfix = '_' + abc_group;
        }
        return postfix;
    }
};

var gpTutorial = function (tutorial_mtime_server, initially_active) {
        this.TYPE_BASIC_TUTORIAL = 1;
        this.TYPE_FARM_TUTORIAL = 2;
        this.TYPE_CURATOR_TUTORIAL = 3;
        var max_step_reached = 0;
        var last_step = -1;
        var last_page = 0;
        var last_data = null;
        var tutwnd = null;
        var tutdata = null;
        var that = this;
        var OnCloseFuncForCurrentPage = null;
        var OnTutorialInitFuncForCurrentPage = null;
        var call_step_init = false;
        var current_quest_id = 0;
        var total_page_counter = 0;
        var nextbutton_flag = false;
        var active = (initially_active) ? true : false;
        var on_update_function_handle = null;
        var arrowsContainer = {};
        this.clearArrowContainer = function () {
            var arrow;
            for (arrow in arrowsContainer) {
                if (arrowsContainer[arrow]) {
                    arrowsContainer[arrow].remove();
                }
                arrowsContainer[arrow] = null;
            }
            arrowsContainer = {};
        };
        this.insertArrowToArrowContainer = function (id, obj) {
            if (arrowsContainer[id]) {
                arrowsContainer[id].remove();
            }
            arrowsContainer[id] = obj;
        };
        this.removeArrowFromArrowContainer = function (id) {
            if (arrowsContainer[id]) {
                arrowsContainer[id].remove();
            }
            arrowsContainer[id] = null;
        };
        this.testArrowContainer = function (id) {
            if (arrowsContainer[id]) {
                return true;
            }
            return false;
        };
        this.returnArrowContainer = function () {
            return arrowsContainer;
        };
        this.quests = {
            'basic_tutorial': 1,
            'farm_town_tutorial': 2
        };

        function reset() {
            that.removeHooks();
            that.clearArrowContainer();
            that.removeAllArrows();
            that.removeAllMenuHighlights();
            if (OnCloseFuncForCurrentPage != null) {
                OnCloseFuncForCurrentPage();
            }
            if (current_quest_id == 2) {
                $('.farm_tut_button').show();
            }
            window.Tutorial = new gpTutorial(0, false);
        }

        function makeNextButton(html_options) {
            html_options = attr(html_options);
            var button = $('<a class="tutorial_arrow_next" ' + html_options + '></a>');
            return button;
        }

        function makePrevButton(html_options) {
            html_options = attr(html_options);
            var button = $('<a class="tutorial_arrow_prev" ' + html_options + '></a>');
            return button;
        }

        function ShowStep() {
            var thisstep = tutdata[last_step],
                tracked_step = (last_step < 9 ? '0' : '') + (last_step + 1),
                tracked_page = (last_page < 9 ? '0' : '') + (last_page + 1),
                tutorial_type = Tutorial.getCurrentQuestType(current_quest_id),
                step_to_track = 'step_' + tracked_step + '-' + tracked_page;
            Kontagent.trackEvent({
                userId: Game.uid,
                eventName: step_to_track,
                subtype1: 'Tutorial',
                subtype2: tutorial_type
            }, function () {});
            if (OnCloseFuncForCurrentPage != null) {
                OnCloseFuncForCurrentPage();
            }
            OnCloseFuncForCurrentPage = null;
            tutwnd.setSize(thisstep.size.width, thisstep.size.height);
            if (thisstep.pages[last_page].pos) {
                tutwnd.setPosition(thisstep.pages[last_page].pos);
            } else if (thisstep.pos) {
                tutwnd.setPosition(thisstep.pos);
            }
            tutwnd.setTitle(thisstep.pages[last_page].title);
            var html = tmpl2(thisstep.pages[last_page].content, {});
            tutwnd.setContent(html);
            if (call_step_init) {
                if (OnTutorialInitFuncForCurrentPage != null) {
                    OnTutorialInitFuncForCurrentPage();
                }
                call_step_init = false;
            }
            var buttondiv = $('<div style="position: absolute; bottom:2px; right:0;">');
            var nextbutton_spawned = false;
            var opts = {};
            if (thisstep.pages.length - last_page > 1 || thisstep.pages[last_page].nextbutton || nextbutton_flag) {
                opts = {};
                opts.href = "#";
                if (thisstep.pages[last_page].nextbutton || nextbutton_flag) {
                    opts.onclick = "Tutorial.requestNextStep();";
                } else {
                    opts.onclick = "Tutorial.nextPageInCurrentStep();";
                }
                opts.style = "float: right;";
                buttondiv.append(makeNextButton(opts));
                nextbutton_spawned = true;
                nextbutton_flag = false;
            }
            if (last_page > 0) {
                var margin_right = nextbutton_spawned ? 10 : 35;
                opts = {};
                opts.href = "#";
                opts.onclick = "Tutorial.prevPageInCurrentStep();";
                opts.style = "float: right; margin-right: " + margin_right + "px;";
                buttondiv.append(makePrevButton(opts));
            }
            tutwnd.appendContent(buttondiv);
            var past_page_count = 1;
            var step_idx = 0;
            for (step_idx; step_idx < last_step; step_idx++) {
                past_page_count += tutdata[step_idx].pages.length;
            }
            past_page_count += last_page;
            var progressbardiv = $('<div style="position: absolute; bottom:2px; left:0;">');
            var progressbar_bg_full = '';
            progressbar_bg_full += '<div class="tutorial_progress_bar">';
            progressbar_bg_full += '<div class="tutorial_progress_bar_progress" style="width:' + (past_page_count / tutdata.page_count) * 206 + 'px;"></div>';
            progressbar_bg_full += '<div class="tutorial_progress_bar_amount bold small">' + past_page_count + '/' + tutdata.page_count + '</div>';
            progressbar_bg_full += '</div>';
            progressbardiv.append($(progressbar_bg_full));
            tutwnd.appendContent(progressbardiv);
            tutwnd.toTop();
        }
        this.onClose = function () {
            reset();
        };
        this.hideCloseButton = function () {
            return tutwnd.getJQCloseButton().hide();
        };
        this.showCloseButton = function () {
            return tutwnd.getJQCloseButton().show();
        };
        this.toTop = function () {
            tutwnd.toTop();
        };
        this.setPositionRelativeToWindow = function (wnd, pos_bias) {
            if (!wnd) {
                return;
            }
            var wnd_offset = wnd.getJQElement().offset(),
                pos_obj = {
                    'left': wnd_offset.left + pos_bias.left,
                    top: wnd_offset.top + pos_bias.top
                };
            Tutorial.setPosition(pos_obj);
        };
        this.setPosition = function (pos) {
            tutwnd.setPosition([pos.left, pos.top]);
        };
        this.wndAction = function (action, quest_id) {
            Layout.ajaxPost('quest', action, {
                quest_id: quest_id
            }, true, function () {
                Kontagent.trackEvent({
                    userId: Game.uid,
                    eventName: 'action_' + action,
                    subtype1: 'Tutorial',
                    subtype2: Tutorial.getCurrentQuestType(quest_id)
                }, function () {});
                if (tutwnd) {
                    tutwnd.getHandler().closable = true;
                    tutwnd.close();
                }
                reset();
                Tutorial.requestBar();
            });
        };
        this.turnOnAndRestart = function (quest_id) {
            Tutorial.wndAction('restart', quest_id);
        };
        this.turnOn = function (quest_id) {
            Tutorial.wndAction('activate', quest_id);
        };
        this.turnOff = function (quest_id) {
            quest_id = quest_id || current_quest_id;
            Kontagent.trackEvent({
                userId: Game.uid,
                eventName: 'turn_off',
                subtype1: 'Tutorial',
                subtype2: Tutorial.getCurrentQuestType(quest_id)
            }, function () {});
            Tutorial.wndAction('deactivate', quest_id);
        };
        this.sendMessage = function (message, args) {
            tutwnd.sendMessage(message, args);
        };
        this.requestBar = function () {
            var b = Layout.getBarBitMask();
            Layout.requestBarSubstreamWithNextNotificationTransmission(b.LAYOUT_BAR_RESOURCES | b.LAYOUT_BAR_FAVOR | b.LAYOUT_BAR_POPULATION | b.LAYOUT_BAR_BUILDING_ORDERS | b.LAYOUT_BAR_UNITS | b.LAYOUT_BAR_PRODUCTION);
            Layout.resetNotificationRequestTimeout(500);
        };
        this.startQuest = function (id) {
            Layout.ajaxPost('quest', 'requestquest', {
                quest_id: id
            }, true, function () {});
        };
        this.getActiveQuest = function () {
            return current_quest_id;
        };
        this.isRepeatingStep = function () {
            return last_step < max_step_reached;
        };
        this.findUnconqueredFarmTownId = function () {
            return WMap.findUnconqueredFarmTownId();
        };
        this.numberOfConqueredFarmTowns = function () {
            var farmtowns_owned_length = $('#map_towns div.farmtown_owned').length;
            return farmtowns_owned_length ? farmtowns_owned_length : 0;
        };
        this.isActive = function () {
            return active;
        };
        this.basicFinishedPage = function () {
            Kontagent.trackEvent({
                userId: Game.uid,
                eventName: 'step_finish',
                subtype1: 'Tutorial',
                subtype2: 'Basic'
            }, function () {});
            tutwnd.setTitle('This task was completed successfully');
            var div = $('<DIV></DIV>');
            div.append('<div id="tutorial_guy"></div>');
            var html_message = '';
            html_message += '<div style="top:6px;position:relative;">';
            html_message += 'Congratulations!' + '<br/>';
            html_message += 'Now you are familiar with the basics of Grepolis.' + '<br/><br/>';
            html_message += 'Please, linger a while longer and I will explain the advantages of the farming villages to you.';
            html_message += '</div>';
            div.append(html_message);
            var str = '<ul style="text-align:center; position: absolute; bottom:0;left:85px;">';
            str += '<li>' + button('Explanation farming village', {
                'style': 'width:250px;',
                'onclick': ' Tutorial.startQuest(2);'
            }, true) + '</li>';
            str += '<li>' + button('Go directly to the game', {
                'style': 'width:250px;',
                'onclick': 'Layout.wnd.getOpenFirst(Layout.wnd.TYPE_QUEST).getHandler().closable = true; Layout.wnd.getOpenFirst(Layout.wnd.TYPE_QUEST).close();'
            }, true) + '</li>';
            str += '</ul>';
            div.append(str);
            tutwnd.setContent(div);
        };
        this.showNextButton = function () {
            nextbutton_flag = true;
        };
        this.setTutorialInitHook = function (fn) {
            OnTutorialInitFuncForCurrentPage = fn;
        };
        this.setOnCloseHook = function (fn) {
            if (fn && typeof fn == 'function') {
                OnCloseFuncForCurrentPage = function () {
                    that.clearArrowContainer();
                    that.removeOnUpdateHandler();
                    fn();
                };
            }
        };
        this.nextPageInCurrentStep = function () {
            var thisstep = tutdata[last_step];
            if (last_page >= thisstep.pages.length) {
                return;
            }
            last_page++;
            ShowStep();
        };
        this.prevPageInCurrentStep = function () {
            if (last_page <= 0) {
                return;
            }
            last_page--;
            ShowStep();
        };
        this.requestNextStep = function (_quest_id) {
            tutwnd.ajaxRequestGet('quest', 'requestnextstep', {
                quest_id: current_quest_id
            }, function () {});
        };
        this.requestFinish = function (_quest_id) {
            tutwnd.ajaxRequestGet('quest', 'requestfinish', {
                quest_id: current_quest_id
            }, function () {
                Kontagent.trackEvent({
                    userId: Game.uid,
                    eventName: 'tutorial_finish',
                    subtype1: 'Tutorial',
                    subtype2: Tutorial.getCurrentQuestType(_quest_id)
                }, function () {});
            });
        };
        this.closeQuestDialog = function () {
            var title = '';
            var text = '';
            switch (parseInt(current_quest_id, 10)) {
            case 1:
            case 2:
                title = 'Finish the tutorial';
                text = s('Would you like to exit the tutorial? %3 (It can be restarted under %1 Settings %2 &#x25ba; %1 Tutorials %2.)', '<span class="bold">', '</span>', '<br/>');
                break;
            default:
                title = 'Cancel the task';
                text = s('Would you like to cancel the task? %3 (It can be restarted under %1 Settings %2 &#x25ba; %1 Tutorials %2.)', '<span class="bold">', '</span>', '<br/>');
                break;
            }
            Layout.showConfirmDialog(title, text, function () {
                Tutorial.turnOff();
            }, 'Yes', 'No');
        };

        function onSetCurrentQuestId() {
            active = parseInt(current_quest_id, 10) != 0;
            switch (parseInt(current_quest_id, 10)) {
            case 2:
                $('.farm_tut_button').hide();
                break;
            default:
                break;
            }
        }
        this.handleNotification = function (notification) {
            var show = true;
            notification.param_str = jQuery.parseJSON(notification.param_str);
            max_step_reached = parseInt(notification.param_str.max_status_reached, 10);
            if (last_step == -1) {
                last_data = notification;
                last_step = parseInt(notification.param_str.status, 10);
                tutwnd = Layout.wnd.Create(Layout.wnd.TYPE_QUEST, "Tutorial");
                call_step_init = true;
                if (current_quest_id != notification.param_id) {
                    tutdata = null;
                    current_quest_id = notification.param_id;
                    onSetCurrentQuestId();
                }
            } else {
                if (last_step == parseInt(notification.param_str.status, 10)) {
                    show = false;
                }
                last_data = notification;
                last_step = parseInt(notification.param_str.status, 10);
                last_page = 0;
                call_step_init = false;
                if (current_quest_id != notification.param_id) {
                    tutdata = null;
                    current_quest_id = notification.param_id;
                    onSetCurrentQuestId();
                }
            }
            if (show) {
                if (!tutdata) {
                    tutwnd.ajaxRequestGet('quest', 'getdata', {
                        quest_id: current_quest_id
                    }, function (_wnd, _data) {
                        tutdata = _data.data;
                        ShowStep();
                    });
                } else {
                    ShowStep();
                }
            }
        };
        this.allow_townindex_close = false;
        this.townindex_onClose = function () {
            return this.allow_townindex_close;
        };
        this.allow_buildingwindow_close = false;
        this.buildingwindow_onClose = function () {
            return this.allow_buildingwindow_close;
        };
        this.removeHooks = function () {
            var wnd;
            wnd = Layout.townIndex.getWnd();
            if (wnd) {
                wnd.sendMessage('unsetOnCloseHook');
            }
            wnd = Layout.buildingWindow.getWnd();
            if (wnd) {
                wnd.sendMessage('unsetOnCloseHook');
            }
        };
        this.removeAllArrows = function () {
            $('.tutorial_arrow').remove();
        };
        this.removeAllMenuHighlights = function () {
            $('.tutorial').each(function () {
                $(this).removeClass('tutorial');
            });
        };
        this.removeMenuHighlights = function (jQElem) {
            jQElem.find('.tutorial').each(function () {
                $(this).removeClass('tutorial');
            });
        };
        this.makeArrow = function (target_elm, direction, offset, parentElm) {
            var tutorialArrowSize = {
                width: 44,
                height: 45
            };
            var elm = null;
            if (typeof (target_elm) == 'string') {
                elm = $(target_elm + ':first');
            } else {
                elm = target_elm;
            }
            var elm_pos = elm.position();
            var top = 0,
                left = 0;
            var bounce_direction = 'down';
            if (!offset) {
                offset = {};
                offset.x = 0;
                offset.y = 0;
            }
            if (parentElm) {
                elm_pos.left = parentElm.offset().left - elm.offset().left;
                elm_pos.top = parentElm.offset().top - elm.offset().top;
            }
            switch (direction) {
            case 'nw':
                top = elm_pos.top + elm.height();
                left = elm_pos.left + elm.width();
                break;
            case 'n':
                top = elm_pos.top + elm.height();
                left = elm_pos.left + ((elm.width() / 2) - (tutorialArrowSize.width / 2));
                bounce_direction = 'up';
                break;
            case 'ne':
                top = elm_pos.top + elm.height();
                left = elm_pos.left - tutorialArrowSize.width;
                break;
            case 'e':
                top = elm_pos.top + ((elm.height() / 2) - (tutorialArrowSize.height / 2));
                left = elm_pos.left - tutorialArrowSize.width;
                bounce_direction = 'right';
                break;
            case 'se':
                top = elm_pos.top - tutorialArrowSize.height;
                left = elm_pos.left - tutorialArrowSize.width;
                break;
            case 's':
                top = elm_pos.top - tutorialArrowSize.height;
                left = elm_pos.left + ((elm.width() / 2) - (tutorialArrowSize.width / 2));
                bounce_direction = 'down';
                break;
            case 'sw':
                top = elm_pos.top - tutorialArrowSize.height;
                left = elm_pos.left + elm.width();
                break;
            case 'w':
                top = elm_pos.top + ((elm.height() / 2) - (tutorialArrowSize.height / 2));
                left = elm_pos.left + elm.width();
                bounce_direction = 'left';
                break;
            }
            var img = document.createElement('img');
            img.src = 'http://cdn.grepolis.com/images/game/tutorial/arrow2_' + direction + '.png';
            img.style.position = 'absolute';
            $(img).css('top', top + offset.y);
            $(img).css('left', left + offset.x);
            $(img).css('z-index', '2000');
            img.id = 'tutorial_arrow';
            $(img).addClass('tutorial_arrow');
            img.remove = function () {
                $(img).remove();
            };
            img.bounce = function () {
                $(img).effect("bounce", {
                    direction: bounce_direction,
                    times: 3,
                    distance: 16
                }, 400, function bouncecallback() {
                    $(this).effect("bounce", {
                        direction: bounce_direction,
                        times: 3,
                        distance: 12
                    }, 400, bouncecallback);
                });
                return img;
            };
            img.blink = function (opt) {
                if (opt.count) {
                    img.blink_count = opt.count;
                } else {
                    img.blink_count = 3;
                }
                if (opt.interval) {
                    img.blink_interval = opt.interval;
                } else {
                    img.blink_interval = 350;
                }
                if (opt.fade) {
                    img.blink_fade = true;
                } else {
                    img.blink_fade = false;
                }
                window.setTimeout(function () {
                    if ($(img).is(':hidden')) {
                        $(img).show();
                        img.blink_count--;
                        if (img.blink_fade) {
                            img.blink_interval /= 2;
                        }
                    } else {
                        $(img).hide();
                    }
                    if (img.blink_count > 0) {
                        window.setTimeout(arguments.callee, img.blink_interval);
                    }
                }, img.blink_interval);
            };
            if (parentElm) {
                parentElm.append(img);
            } else {
                elm.parent().append(img);
            }
            return img;
        };
        this.setOnUpdateHandler = function (callback, interval) {
            if (callback && typeof callback == 'function') {
                that.removeOnUpdateHandler();
                on_update_function_handle = window.setInterval(callback, interval || 100);
            }
        };
        this.removeOnUpdateHandler = function () {
            if (on_update_function_handle) {
                window.clearInterval(on_update_function_handle);
                on_update_function_handle = null;
            }
        };
        this.getCurrentQuestType = function (quest_id) {
            if (quest_id == 1) {
                return 'Basic';
            } else if (quest_id == 2) {
                return 'Farm';
            } else if (quest_id == 3) {
                return 'Curator';
            } else {
                return 'None';
            }
        }
    };

var FacebookConnect = {
    init: function (callback) {
        if ('undefined' != typeof (FB)) {
            FB_RequireFeatures(["XFBML"], function () {
                FB.init("110b6519781022bbeac87c7a1df350eb", "/xd_receiver.htm");
                if (typeof callback == 'function') {
                    callback();
                }
            });
        }
    },
    publishStream: function (message, attachment, action_links, usertext, callback) {
        FB.ensureInit(function () {
            FB.Connect.requireSession(function () {
                FB.Connect.ifUserConnected(function () {
                    if (FB.Connect.get_loggedInUser) {
                        FB.Connect.streamPublish(message, attachment, action_links, null, usertext, callback);
                    }
                });
            });
        });
    },
    initDialog: function () {
        $("#facebook_dialog_connect").dialog({
            autoOpen: false,
            height: 'auto',
            width: 450,
            resizable: false,
            draggable: false,
            modal: true
        });
    }
}

var FileUploadForm = {
    last_iframe_id: 0,
    callbacks: {},
    createiFrame: function (parent, id) {
        var iframe = $('<iframe src="javascript:false;" name="' + id + '" id="' + id + '" onload="FileUploadForm.iFrameLoaded(\'' + id + '\')"></iframe>');
        $(iframe).css('display', 'none');
        $(parent).append(iframe);
    },
    createForm: function (parent, id, iframe_id, url) {
        var form = $('<form id="' + id + '" method="post" enctype="multipart/form-data"></form>');
        $(form).attr('action', url);
        $(form).attr('target', iframe_id);
        $(parent).append(form);
    },
    createFileUploadForm: function (parent, url) {
        var local_iframe_id = FileUploadForm.last_iframe_id++;
        FileUploadForm.createiFrame(parent, 'iframe_' + local_iframe_id);
        FileUploadForm.createForm(parent, 'form_' + local_iframe_id, 'iframe_' + local_iframe_id, url);
    },
    createFileUploadFormEx: function (parent, url, form_id) {
        var local_iframe_id = FileUploadForm.last_iframe_id++;
        FileUploadForm.createiFrame(parent, 'iframe_' + local_iframe_id);
        FileUploadForm.createForm(parent, form_id, 'iframe_' + local_iframe_id, url);
    },
    hookOnExsitingForm: function (form, callback) {
        var local_iframe_id = FileUploadForm.last_iframe_id++;
        FileUploadForm.registerCallback(callback, 'iframe_' + local_iframe_id);
        FileUploadForm.createiFrame($(form).parent(), 'iframe_' + local_iframe_id);
        $(form).attr('target', 'iframe_' + local_iframe_id);
        $(form).attr('method', 'post');
        $(form).attr('enctype', 'multipart/form-data');
    },
    iFrameLoaded: function (iframe_id) {
        FileUploadForm.callCallback(iframe_id);
    },
    registerCallback: function (callback, iframe_id) {
        FileUploadForm.callbacks[iframe_id] = new Object();
        FileUploadForm.callbacks[iframe_id].callback = callback;
        FileUploadForm.callbacks[iframe_id].fired = 0;
        FileUploadForm.callbacks[iframe_id].suppressed = 0;
    },
    callCallback: function (iframe_id) {
        if (FileUploadForm.callbacks[iframe_id]) {
            if (FileUploadForm.callbacks[iframe_id].suppressed == 0) {
                FileUploadForm.callbacks[iframe_id].suppressed++;
                return;
            }
            FileUploadForm.callbacks[iframe_id].fired++;
            FileUploadForm.callbacks[iframe_id].callback();
        }
    }
}

var Hideout = {
    slider: null,
    notifyLock: false,
    init: function (min, max) {
        this.initSlider();
        this.slider.setValue(0);
        this.slider.setMin(min);
        this.slider.setMax(max < 0 ?
        function () {
            $('#hide_order_confirm').remove();
            Hideout.slider.disable(true);
            return 0;
        }.call() : max);
        $(document).unbind('notification.dataChangedHide', Hideout.notificationEventHandler);
        $(document).bind('notification.dataChangedHide', Hideout.notificationEventHandler);
    },
    notificationEventHandler: function (evt, data) {
        var wndIsOpen = (Layout.buildingWindow.getWnd() != null && Layout.buildingWindow.getWnd().getHandler().currentBuilding == 'hide');
        if (!wndIsOpen) {
            Hideout.notifyLock = false;
            $(document).unbind('notification.dataChangedHide', Hideout.notificationEventHandler);
        } else {
            if (!Hideout.notifyLock) {
                var params = $.parseJSON(data.param_str);
                if (params.espionage_finished) {
                    Hideout.action('index', {});
                } else if (params.espionage_storage) {
                    $('#hide_espionage span.hide_storage_level').html(params.espionage_storage);
                }
            } else {
                Hideout.notifyLock = false;
            }
        }
    },
    changeCount: function (e) {
        var count = parseInt(document.getElementById('hide_order_input').value, 10);
        if (isNaN(count)) {
            return;
        }
        $('#hide_order_confirm').css('visibility', count ? '' : 'hidden');
    },
    initSlider: function () {
        var element_slider = $('#hide_order_slider');
        this.slider = new Slider({
            elementMin: $('#hide_order_min'),
            elementMax: $('#hide_order_max'),
            elementDown: $('#hide_order_down'),
            elementUp: $('#hide_order_up'),
            elementInput: $('#hide_order_input'),
            elementSlider: element_slider
        });
        element_slider.bind('change', function () {
            Hideout.changeCount(Hideout.slider.getValue());
        });
    },
    action: function (action, params) {
        Layout.ajaxPost('building_hide', action, params, true, function (data) {
            $('#hide_espionage').parent().html($(data.html));
        });
    },
    storeIron: function (amount) {
        Hideout.notifyLock = true;
        Hideout.action('store_iron', {
            'amount': amount
        });
    }
};

var MenuBubbleMovement = {
    unit_movements: null,
    spying: null,
    colonization_commands: null,
    revolts: null,
    conqueror_units: null,
    commands_count: 0,
    attack_commands_count: 0,
    menu_bubble: null,
    renderHtml: function (data) {
        if (data.json.movement) {
            if (data.tmpl.movement != null) {
                GameData.add({
                    'MenuBubbleMovementTemplate': data.tmpl.movement
                });
            } else {
                data.tmpl.movement = GameData.MenuBubbleMovementTemplate;
                if (GameData.MenuBubbleMovementTemplate == null || GameData.MenuBubbleMovementTemplate == undefined) {
                    return '';
                }
            }
            MenuBubbleMovement.unit_movements = data.json.movement.movements;
            MenuBubbleMovement.spying = data.json.movement.spying;
            MenuBubbleMovement.colonization_commands = data.json.movement.colonization_commands;
            MenuBubbleMovement.revolts = data.json.movement.revolts;
            MenuBubbleMovement.conqueror_units = data.json.movement.conqueror_units;
            return tmpl2(data.tmpl.movement, data.json.movement);
        }
        return '';
    },
    redrawCommandCounts: function () {
        if (MenuBubbleMovement.menu_bubble) {
            var new_height = MenuBubbleMovement.commands_count * 50;
            new_height = new_height > 370 ? 370 : new_height;
            MenuBubbleMovement.menu_bubble.changeHeight(new_height);
            MenuBubbleMovement.menu_bubble.getjIconElem().html(MenuBubbleMovement.commands_count).css({
                color: (MenuBubbleMovement.attack_commands_count > 0 ? '#f00' : '#fff')
            });
        }
    },
    finishCountdownCallback: function (data) {
        if (data && data.incoming != undefined && data.incoming) {
            if (data.incoming_attack) {
                MenuBubbleMovement.attack_commands_count--;
            }
            var bits = Layout.getBarBitMask();
            Layout.requestBarSubstreamWithNextNotificationTransmission(bits.LAYOUT_BAR_RESOURCES | bits.LAYOUT_BAR_UNITS | bits.LAYOUT_BAR_MENU_BUBBLES);
        }
        var ul_item = $(this).parent().parent().parent();
        $(this).parent().parent().remove();
        MenuBubbleMovement.commands_count--;
        if (MenuBubbleMovement.commands_count <= 0) {
            MenuBubbleMovement.commands_count = 0;
            ul_item.html('No movements');
        }
        MenuBubbleMovement.redrawCommandCounts();
        Layout.resetNotificationRequestTimeout(5000);
        GPEvents.global.trigger(GPEvents.global.town.units_changed);
        GPEvents.global.trigger(GPEvents.global.town.resources_update);
        window.setTimeout("MenuBubbleMovement.menu_bubble.forceUpdate();", 2500);
    },
    cancelEspionage: function (spy_id) {
        Layout.ajaxPost('command_info', 'cancel_espionage', {
            id: spy_id
        }, false, function () {
            $('#spy_movement_' + spy_id).remove();
            MenuBubbleMovement.commands_count--;
            MenuBubbleMovement.redrawCommandCounts();
        });
    },
    cancelCommand: function (command_id) {
        Layout.ajaxPost('command_info', 'cancel_command', {
            id: command_id
        }, false, function (data, flag) {});
    },
    initPopups: function () {
        var unit_movements_img = $('#unit_movements img');
        var spy_movements_img = $('#spy_movements img');
        var colonization_img = $('#colonization_commands img');
        var conqueror_img = $('#conqueror_units div img');
        var revolts_in_current_town_arising_img = $('#revolts_in_current_town li.arising img.command_type');
        var revolts_in_current_town_running_img = $('#revolts_beyond li.arising img.command_type');
        var revolts_beyond_arising_img = $('#revolts_beyond li.arising img.command_type');
        var revolts_beyond_running_img = $('#revolts_beyond li.running img.command_type');
        unit_movements_img.each(function () {
            var command_name = $(this).attr('alt');
            $(this).attr('alt', '');
            $(this).mousePopup(new MousePopup(command_name));
        });
        spy_movements_img.each(function () {
            var command_name = $(this).attr('alt');
            $(this).attr('alt', '');
            $(this).mousePopup(new MousePopup(command_name));
        });
        colonization_img.each(function () {
            var popup_text = $(this).attr('alt');
            $(this).attr('alt', '');
            $(this).mousePopup(new MousePopup(popup_text));
        });
        conqueror_img.each(function () {
            $(this).mousePopup(new MousePopup("The conquest has started"));
        });
        revolts_in_current_town_arising_img.each(function () {
            $(this).mousePopup(new MousePopup("A revolt is being started"));
        });
        revolts_in_current_town_running_img.each(function () {
            $(this).mousePopup(new MousePopup("A revolt has started"));
        });
        revolts_beyond_arising_img.each(function () {
            $(this).mousePopup(new MousePopup("A revolt is being started"));
        });
        revolts_beyond_running_img.each(function () {
            $(this).mousePopup(new MousePopup("A revolt has started"));
        });
    },
    startCountdown: function () {
        if (MenuBubbleMovement.unit_movements == null || MenuBubbleMovement.unit_movements.length > 0) {
            var unit_movements = $('#unit_movements span.eta');
            unit_movements.each(function (i, span) {
                span = $(span);
                span.countdown(MenuBubbleMovement.unit_movements[i].arrival_at, {});
                span.bind('finish', function () {
                    MenuBubbleMovement.finishCountdownCallback.apply(this, [MenuBubbleMovement.unit_movements[i]]);
                });
            });
        }
        if (MenuBubbleMovement.spying != null) {
            $.each(MenuBubbleMovement.spying, function (id, item) {
                var span = $('#spy_movement_' + id + ' span.eta');
                span.countdown(item.arrival_at, {});
                span.bind('finish', function () {
                    MenuBubbleMovement.finishCountdownCallback.apply(this, [item]);
                });
            });
        }
        if (MenuBubbleMovement.colonization_commands != null && MenuBubbleMovement.colonization_commands.length > 0) {
            var colonization_cmds = $('#colonization_commands span.eta');
            colonization_cmds.each(function (i, span) {
                var colonization_data = MenuBubbleMovement.colonization_commands[i];
                var eta;
                if (colonization_data.colonization_finished_eta === undefined) {
                    eta = colonization_data.arrival_at;
                } else {
                    eta = colonization_data.colonization_finished_at;
                }
                span = $(span);
                span.countdown(eta, {});
                span.bind('finish', function () {
                    MenuBubbleMovement.finishCountdownCallback.apply(this, [MenuBubbleMovement.colonization_commands[i]]);
                });
            });
        }
        if (MenuBubbleMovement.conqueror_units != null && MenuBubbleMovement.conqueror_units.length > 0) {
            var conqueror_units_spans = $('#conqueror_units span.eta');
            conqueror_units_spans.each(function (i, span) {
                var conquest_data = MenuBubbleMovement.conqueror_units[i];
                var eta;
                eta = conquest_data.conquest_finished_at;
                span = $(span);
                span.countdown(eta, {});
                span.bind('finish', function () {
                    MenuBubbleMovement.finishCountdownCallback.apply(this, [MenuBubbleMovement.conqueror_units[i]]);
                });
            });
        }
        if (MenuBubbleMovement.revolts != null && MenuBubbleMovement.revolts.count > 0) {
            var eta, revolt_data;
            var revolts_in_current_town_arising = $('#revolts_in_current_town div.arising span.eta');
            revolts_in_current_town_arising.each(function (i, span) {
                revolt_data = MenuBubbleMovement.revolts.in_current_town.arising[i];
                eta = revolt_data.started_at;
                span = $(span);
                span.countdown(eta, {});
                span.bind('finish', function () {
                    MenuBubbleMovement.finishCountdownCallback.apply(this, [MenuBubbleMovement.revolts.in_current_town.arising[i]]);
                });
            });
            var revolts_in_current_town_running = $('#revolts_in_current_town div.running span.eta');
            revolts_in_current_town_running.each(function (i, span) {
                revolt_data = MenuBubbleMovement.revolts.in_current_town.running[i];
                eta = revolt_data.finished_at;
                span = $(span);
                span.countdown(eta, {});
                span.bind('finish', function () {
                    MenuBubbleMovement.finishCountdownCallback.apply(this, [MenuBubbleMovement.revolts.in_current_town.running[i]]);
                });
            });
            var revolts_beyond_arising = $('#revolts_beyond div.arising span.eta');
            revolts_beyond_arising.each(function (i, span) {
                revolt_data = MenuBubbleMovement.revolts.beyond.arising[i];
                eta = revolt_data.started_at;
                span = $(span);
                span.countdown(eta, {});
                span.bind('finish', function () {
                    MenuBubbleMovement.finishCountdownCallback.apply(this, [MenuBubbleMovement.revolts.beyond.arising[i]]);
                });
            });
            var revolts_beyond_running = $('#revolts_beyond div.running span.eta');
            revolts_beyond_running.each(function (i, span) {
                revolt_data = MenuBubbleMovement.revolts.beyond.running[i];
                eta = revolt_data.finished_at;
                span = $(span);
                span.countdown(eta, {});
                span.bind('finish', function () {
                    MenuBubbleMovement.finishCountdownCallback.apply(this, [MenuBubbleMovement.revolts.beyond.running[i]]);
                });
            });
        }
    },
    updateMovementBubble: function () {
        if (MenuBubbleMovement.unit_movements == null || MenuBubbleMovement.unit_movements.length > 0) {
            $.each(MenuBubbleMovement.unit_movements, function (id, item) {
                if (item.started_at != null && item.started_at < Timestamp.now() - 600) {
                    var span = $('#cmd_movement_' + item.id + ' div.menu_bubble_cancelable a');
                    span.remove();
                }
            });
        }
        if (MenuBubbleMovement.spying != null) {
            $.each(MenuBubbleMovement.spying, function (id, item) {
                if (item.started_at != null && item.started_at < (Timestamp.now() - 300)) {
                    var span = $('#spy_movement_' + id + ' div.menu_bubble_cancelable a');
                    span.remove();
                }
            });
        }
    }
};

var MenuBubbleTrade = {
    trades: null,
    trades_count: 0,
    menu_bubble: null,
    renderHtml: function (data) {
        if (data.json.trade) {
            if (data.tmpl.trade != null) {
                GameData.add({
                    'MenuBubbleTradeTemplate': data.tmpl.trade
                });
            } else {
                data.tmpl.trade = GameData.MenuBubbleTradeTemplate;
                if (GameData.MenuBubbleTradeTemplate == null || GameData.MenuBubbleTradeTemplate == undefined) {
                    return '';
                }
            }
            MenuBubbleTrade.trades = data.json.trade.trades;
            return tmpl2(data.tmpl.trade, data.json.trade);
        }
        return '';
    },
    cancelTrade: function (trade_id) {
        Layout.ajax('trade', 'cancel', {
            id: trade_id
        }, true, function () {
            MenuBubbleTrade.menu_bubble.forceUpdate();
        });
    },
    redrawTradeCount: function () {
        if (MenuBubbleTrade.menu_bubble) {
            var new_height = MenuBubbleTrade.trades_count * 50;
            new_height = new_height > 370 ? 370 : new_height;
            MenuBubbleTrade.menu_bubble.changeHeight(new_height);
            MenuBubbleTrade.menu_bubble.getjIconElem().html(MenuBubbleTrade.trades_count);
        }
    },
    finishCountdownCallback: function () {
        var entry = $(this).parent().parent();
        var ul_item = entry.parent();
        entry.remove();
        MenuBubbleTrade.trades_count--;
        if (MenuBubbleTrade.trades_count <= 0) {
            MenuBubbleTrade.trades_count = 0;
            ul_item.html('No trade');
        }
        MenuBubbleTrade.redrawTradeCount();
        var bits = Layout.getBarBitMask();
        Layout.requestBarSubstreamWithNextNotificationTransmission(bits.LAYOUT_BAR_RESOURCES);
        Layout.resetNotificationRequestTimeout(2000);
    },
    startCountdown: function () {
        if (MenuBubbleTrade.trades != null && MenuBubbleTrade.trades.length > 0) {
            var trades = $('#trades span.eta');
            trades.each(function (i, span) {
                var data = MenuBubbleTrade.trades[i];
                span = $(span);
                span.countdown(data.arrival_at, {});
                span.bind('finish', MenuBubbleTrade.finishCountdownCallback);
                var resources = ['wood', 'stone', 'iron'];
                var popup_text = "";
                $.each(data, function (key, value) {
                    if (jQuery.inArray(key, resources) != -1 && value > 0) {
                        popup_text += '<img src="http://cdn.grepolis.com/images/game/res/' + key + '.png" alt=""/> ' + value + "<br />";
                    }
                });
                popup_text += s("Arrival: %1", readableUnixTimestamp(data.arrival_at_utc, 'no_offset'));
                span.parent().parent().mousePopup(new MousePopup(popup_text));
            });
        }
    }
};

function getUpgradeBuildingData(building, full_queue, description) {
    var res = ITowns.getTown(Game.townId).resources();
    var result = '<h3 style="margin: 10px 0;">' + building.name + ' (' + building.level + ')</h3>';
    var enough_population = true;
    var enough_resources = true;
    var max_reached = false;
    var not_possible = false;
    if (building.max_level) {
        max_reached = true;
        not_possible = true;
        result += '%1 has reached the maximum expansion level.'.replace('%1', building.name) + '<br />';
    } else {
        var reqInQuestion = {
            wood: {
                name: 'Wood',
                amount: building.needed_resources.wood
            },
            stone: {
                name: 'Rock',
                amount: building.needed_resources.stone
            },
            iron: {
                name: 'Silver coins',
                amount: building.needed_resources.iron
            },
            population: {
                name: 'Food',
                amount: building.pop
            },
            time: {
                name: 'Time',
                amount: building.build_time
            }
        };
        var current_production = ITowns.getTown(Game.townId).production();
        var time_to_build = 0;
        result += '<strong>' + 'Expansion costs' + '</strong><br />';
        jQuery.each(reqInQuestion, function (reqId, req) {
            result += '<img src="' + Game.img() + '/game/res/' + (reqId == 'population' ? 'pop' : reqId) + '.png" alt="' + req.name + '" />';
            if (req.amount > res[reqId]) {
                not_possible = true;
                if (reqId != 'time') {
                    if (reqId == 'population') {
                        if (req.amount > 0) {
                            enough_population = false;
                        } else {
                            not_possible = false;
                        }
                    } else if (reqId != 'population') {
                        enough_resources = false;
                        if (current_production[reqId] > 0) {
                            var time = parseInt(3600 * parseFloat((req.amount - res[reqId]) / current_production[reqId]), 10);
                            if (time_to_build < time && time > 0) {
                                time_to_build = time;
                            }
                        }
                    }
                }
                result += '<span' + (not_possible ? ' style="color:#B00"' : '') + '>' + req.amount + '</span>';
            } else {
                result += req.amount;
            }
        });
        result += '<br/>';
        if ($(building.get_dependencies).length) {
            not_possible = true;
            result += 'Required:' + '<br />';
            jQuery.each(building.get_dependencies, function () {
                result += '<span class="requirement">' + '%1 Level %2'.replace('%2', this.needed_level).replace('%1', this.name) + '</span><br />';
            });
        }
        if (!enough_population) {
            not_possible = true;
            result += '<span class="requirement">' + 'Not enough farm spaces.' + '</span><br />';
        }
        if (!enough_resources) {
            not_possible = true;
            if (!building.enough_storage) {
                result += '<span class="requirement">' + 'Your warehouse is too small.' + '</span><br />';
            } else {
                result += '<span class="requirement">' + 'Not enough resources.' + '</span><br />' + '<span class="requirement">' + 'Enough resources: %1.'.replace('%1', formatDateTimeNice(Timestamp.server() + time_to_build, false)) + '</span><br />';
            }
        }
        if (full_queue) {
            not_possible = true;
            result += 'No further orders possible.' + '<br />';
        }
    }
    if (description !== false) {
        result += '<p style="width: 320px;">' + building.description + '</p>';
    }
    return [result, max_reached, not_possible];
}

function formatResources(resources, separator) {
    if (separator === undefined) {
        separator = ' ';
    }
    var html = '',
        res;
    for (res in GameData.resources) {
        html += '<img src="' + Game.img() + '/game/res/' + res + '.png" alt=""/> ' + resources[res] + separator;
    }
    return html;
}

function hasValidTownToken(rx_data) {
    return (rx_data && rx_data.t_token && Game.townId && rx_data.t_token == Game.townId);
}

function initializeMenuBubbles(menu_bubbles_data) {
    var BubbleConstructor = Layout instanceof GPLayout ? menuBubble : MenuBubbleB;
    MenuBubbleOrders.menu_bubble = new BubbleConstructor('troops', 68, 380, 170, 1, function (evt, data) {
        var elm = data;
        var callback = function (ret_data) {
                MenuBubbleOrders.orders_count = ret_data.orders_count || 0;
                MenuBubbleOrders.redrawOrderCount();
                elm.html(MenuBubbleOrders.renderHtml(ret_data));
            };
        if (evt.type == 'this.open') {
            if (elm.html() != '' && !evt.forceUpdate) {
                return;
            }
            var params = {
                'send_tmpl': !(GameData.MenuBubbleOrdersTemplate != null && GameData.MenuBubbleOrdersTemplate != undefined)
            };
            Layout.ajaxGet('menu_bubble', 'troops', params, false, callback);
        } else if (evt.type == 'this.inject') {
            callback(evt.injection_data);
        }
    });
    MenuBubbleMovement.menu_bubble = new BubbleConstructor('movement', 68, 409, 180, 1, function (evt, data) {
        var elm = data;
        var callback = function (ret_data) {
                var bubble = MenuBubbleMovement;
                bubble.commands_count = ret_data.commands_count || 0;
                bubble.attack_commands_count = ret_data.attack_commands_count || 0;
                bubble.redrawCommandCounts();
                elm.html(bubble.renderHtml(ret_data));
                GPEvents.global.trigger(GPEvents.global.town.commands_updated);
            };
        if (evt.type == 'this.open') {
            if (elm.html() != '' && !evt.forceUpdate) {
                return;
            }
            var params = {
                'send_tmpl': !(GameData.MenuBubbleMovementTemplate != null && GameData.MenuBubbleMovementTemplate != undefined)
            };
            Layout.ajaxGet('menu_bubble', 'movement', params, false, callback);
        } else if (evt.type == 'this.inject') {
            callback(evt.injection_data);
        }
    });
    MenuBubbleTrade.menu_bubble = new BubbleConstructor('trade', 68, 438, 190, 1, function (evt, data) {
        var elm = data;
        var callback = function (ret_data) {
                MenuBubbleTrade.trades_count = ret_data.trade_count || 0;
                MenuBubbleTrade.redrawTradeCount();
                elm.html(MenuBubbleTrade.renderHtml(ret_data));
            };
        if (evt.type == 'this.open') {
            if (elm.html() != '' && !evt.forceUpdate) {
                return;
            }
            var params = {
                'send_tmpl': !(GameData.MenuBubbleTradeTemplate != null && GameData.MenuBubbleTradeTemplate != undefined)
            };
            Layout.ajaxGet('menu_bubble', 'trade', params, false, callback);
        } else if (evt.type == 'this.inject') {
            callback(evt.injection_data);
        }
    });
    $(document).bind({
        'notification.menububbleTroops': notificationEventHandler,
        'notification.menububbleMovement': notificationEventHandler,
        'notification.menububbleTrade': notificationEventHandler,
        'notification.menububbleReloadAll': notificationEventHandler
    });
    GPEvents.global.bind(GPEvents.global.town.town_switch, notificationEventHandler);

    function notificationEventHandler(event, notification) {
        switch (event.namespace || event.type) {
        case 'menububbleTroops':
            MenuBubbleOrders.menu_bubble.forceUpdate();
            break;
        case 'menububbleMovement':
            MenuBubbleMovement.menu_bubble.forceUpdate();
            break;
        case 'menububbleTrade':
            MenuBubbleTrade.menu_bubble.forceUpdate();
            break;
        case 'townSwitch':
        case 'menububbleReloadAll':
            var params = {
                'send_tmpl': !((GameData.MenuBubbleTradeTemplate != null && GameData.MenuBubbleTradeTemplate != undefined) || (GameData.MenuBubbleMovementTemplate != null && GameData.MenuBubbleMovementTemplate != undefined) || (GameData.MenuBubbleOrdersTemplate != null && GameData.MenuBubbleOrdersTemplate != undefined))
            };
            Layout.ajaxGet('menu_bubble', 'get_all', params, false, function (ret_data) {
                MenuBubbleMovement.menu_bubble.injectData(ret_data);
                MenuBubbleOrders.menu_bubble.injectData(ret_data);
                MenuBubbleTrade.menu_bubble.injectData(ret_data);
            });
            break;
        }
    }
    if (menu_bubbles_data) {
        MenuBubbleMovement.menu_bubble.injectData(menu_bubbles_data);
        MenuBubbleOrders.menu_bubble.injectData(menu_bubbles_data);
        MenuBubbleTrade.menu_bubble.injectData(menu_bubbles_data);
    } else {
        GPEvents.global.trigger(GPEvents.global.town.town_switch);
    }
}

function menuBubble(name, ptop, pleft, c_openwidth, c_openheight, eventCallback) {
    var iconelm, jiconelm;
    var contelm, jcontelm;
    var isOpen = false;
    var close_running = false;
    var should_be_open = false;
    var that = this;
    iconelm = document.createElement('a');
    jiconelm = $(iconelm);
    contelm = document.createElement('div');
    jcontelm = $(contelm);
    var iconelm_width = c_openwidth;
    jiconelm.attr({
        'id': 'icon_' + name,
        'class': 'town_control',
        'href': '#'
    });
    jcontelm.css({
        'position': 'absolute',
        'top': ptop,
        'left': pleft,
        'width': iconelm_width,
        'height': 0,
        'z-index': 5000,
        'display': 'none'
    });
    jcontelm.attr('id', 'lbox_cont_' + name);
    jcontelm.html('');
    iconelm._cont = contelm;
    iconelm._jcont = jcontelm;
    this.eventCallback = eventCallback;
    this.getjIconElem = function () {
        return jiconelm;
    };
    this.changeHeight = function (height) {
        c_openheight = height;
        jcontelm.height(height);
    };
    this.forceUpdate = function () {
        if (eventCallback) {
            eventCallback.call(that, {
                type: 'this.open',
                forceUpdate: true
            }, jcontelm);
        }
    };
    this.injectData = function (data) {
        if (eventCallback) {
            eventCallback.call(that, {
                type: 'this.inject',
                injection_data: data
            }, jcontelm);
        }
    };

    function closeContent() {
        if (should_be_open) {
            return;
        }
        if (close_running || !isOpen) {
            return;
        }
        close_running = true;
        isOpen = false;
        close_running = false;
        should_be_open = false;
        jcontelm.hide();
        if (eventCallback) {
            eventCallback.call(that, {
                type: 'this.close'
            }, jcontelm);
        }
    }

    function openContent() {
        if (!should_be_open) {
            return;
        }
        if (isOpen) {
            return;
        }
        isOpen = true;
        if (eventCallback) {
            eventCallback.call(that, {
                'type': 'this.open',
                'forceUpdate': false
            }, jcontelm);
        }
        jcontelm.show();
        if (jcontelm.attr('id') == 'lbox_cont_movement') {
            MenuBubbleMovement.updateMovementBubble();
        }
    }

    function enterIconHandler(event) {
        if (WMap.currently_scrolling) {
            return;
        }
        document.body.style.cursor = 'e-resize';
        should_be_open = true;
        window.setTimeout(function () {
            openContent()
        }, 160);
    }

    function leaveIconHandler() {
        should_be_open = false;
        document.body.style.cursor = 'default';
        window.setTimeout(function () {
            closeContent();
        }, 200);
    }

    function enterContentHandler() {
        should_be_open = true;
    }

    function leaveContentHandler() {
        should_be_open = false;
        window.setTimeout(function () {
            closeContent();
        }, 200);
    }
    Layout.bindMenuBubbleClickHandler(name, jiconelm);
    jiconelm.hover(enterIconHandler, leaveIconHandler);
    jcontelm.hover(enterContentHandler, leaveContentHandler);
    document.getElementById('town_controls').appendChild(iconelm);
    $('body').append(contelm);
}

function MenuBubbleB(type, ptop, pleft, c_openwidth, c_openheight, callback) {
    var that = this,
        container = $('<div>').appendTo('#units_sidebar'),
        icon = $('#switch_' + type),
        is_open = false,
        close_running = false;
    container.css({
        'display': 'none'
    }).attr('id', type + '_sidebar');
    this.render = function () {
        container.show();
        if (is_open) {
            return;
        }
        is_open = true;
        if (callback) {
            callback.call(that, {
                type: 'this.open',
                forceUpdate: false
            }, container);
        }
        container.show();
        if (container.attr('id') == 'lbox_cont_movement') {
            MenuBubbleMovement.updateMovementBubble();
        }
    };
    this.update = this.render;
    this.destroy = function () {
        if (close_running || !is_open) {
            return;
        }
        close_running = true;
        is_open = false;
        close_running = false;
        container.hide();
        if (callback) {
            callback.call(that, {
                type: 'this.close'
            }, container);
        }
    };
    this.getjIconElem = function () {
        return icon;
    };
    this.changeHeight = function () {
        return false;
    };
    this.forceUpdate = function () {
        if (callback) {
            callback.call(that, {
                type: 'this.open',
                forceUpdate: true
            }, container);
        }
    };
    this.injectData = function (data) {
        if (callback) {
            callback.call(that, {
                type: 'this.inject',
                injection_data: data
            }, container);
        }
    };
}

var PhoenicianSalesman = {
    data: null,
    sliders: [],
    resource_names: null,
    call_for_gold_dialog_text: null,
    hint_showed: false,
    initialize: function (json_data) {
        var root = $('#ph_offers');
        PhoenicianSalesman.data = json_data;
        PhoenicianSalesman.initializeMousePopups();
        root.find('a.confirm').click(this.trade);
        root.find('div.ph_order_info').each(function () {
            PhoenicianSalesman.initSlider.call($(this));
        });
    },
    initializeMousePopups: function () {
        $('#current_town_span_id').mousePopup(new MousePopup('City that the Phoenician merchant is currently visiting!'));
        $('#next_town_span_id').mousePopup(new MousePopup('City that the Phoenician merchant will visit next!'));
        $.each(PhoenicianSalesman.data.goods.units, function (idx, unit) {
            $('#ph_unit_order_info_' + idx + ' form.ph_unit_order_count').setPopup(unit.name);
        });
    },
    startArrivesAtCountdown: function (until) {
        $('#eta_arrival_at').countdown(until, {}).bind('finish', function () {
            PhoenicianSalesman.reloadContent();
        });
    },
    startLeavesAtCountdown: function (until) {
        $('#eta_departure_at').countdown(until, {}).bind('finish', function () {
            PhoenicianSalesman.reloadContent();
        });
    },
    reloadContent: function () {
        var params = {
            'tab': PhoenicianSalesman.data.tab
        };
        Layout.ajaxGet('phoenician_salesman', 'load_content', params, true, function (data) {
            $('div.phoenician_salesman_background').parent().html($(data.html));
        });
    },
    action: function (action, opts) {
        Layout.ajaxPost('phoenician_salesman', action, opts, true, function (data) {
            $('div.phoenician_salesman_background').parent().html($(data.html));
        });
    },
    set_next_town: function (id) {
        PhoenicianSalesman.action('set_next_town', {
            'next_town_id': id
        });
    },
    doCallToTown: function () {
        Layout.ajaxPost('phoenician_salesman', 'immediate_call_for_gold', {}, true, function () {
            var b_wnd = Layout.buildingWindow.getWnd();
            if (b_wnd) {
                Layout.buildingWindow.refresh();
            }
            var ti_wnd = Layout.townIndex.getWnd();
            if (ti_wnd != null) {
                ti_wnd.sendMessage('togglePhoenicianSalesmanAndReRender');
            }
            Kontagent.trackEvent({
                userId: Game.uid,
                eventName: 'call_trader_for_gold',
                subtype1: Game.market_id,
                subtype2: Game.world_id,
                subtype3: 'buy_pa_feature'
            }, function () {});
            var ev = GPEvents.global;
            ev.trigger(ev.spend_gold);
        });
    },
    callToTown: function () {
        Layout.showConfirmDialog('Summon Phoenician merchant now?', PhoenicianSalesman.call_for_gold_dialog_text, function () {
            PhoenicianSalesman.doCallToTown();
        }, null, null, null, 'Do not show this window again');
    },
    moveOn: function () {
        Layout.ajaxPost('phoenician_salesman', 'move_on', {}, true, function (data) {
            $('div.phoenician_salesman_background').parent().html($(data.html));
            var b_wnd = Layout.buildingWindow.getWnd();
            if (b_wnd) {
                Layout.buildingWindow.refresh();
            }
            var ti_wnd = Layout.townIndex.getWnd();
            if (ti_wnd != null) {
                ti_wnd.sendMessage('togglePhoenicianSalesmanAndReRender');
            }
        });
    },
    trade: function () {
        var values = $(this).parent().find('input'),
            params = {},
            amount = ~~values[1].value;
        if (!amount) {
            return;
        }
        params[values[0].name + '_name'] = values[0].value;
        params[values[0].name + '_amount'] = amount;
        PhoenicianSalesman.action('trade_' + values[0].name + 's', params);
    },
    initSlider: function () {
        var slider_div = this.find('div.ph_unit_order_slider');
        var slider = new Slider({
            elementMin: this.find('.ph_unit_order_min'),
            elementMax: this.find('.ph_unit_order_max'),
            elementDown: this.find('a.ph_unit_order_down'),
            elementUp: this.find('a.ph_unit_order_up'),
            elementInput: this.find('input.ph_unit_order_input'),
            elementSlider: slider_div
        });
        var inp = this.find('input.ph_unit_order_unit_hidden'),
            id = ~~inp.parents('div')[0].id.match(/\d+/)[0],
            property = PhoenicianSalesman.data.goods[inp[0].name + 's'][id],
            text, res = inp[0].name === 'resource',
            that = this;
        slider_div.bind('change', res ?
        function () {
            PhoenicianSalesman.resourceShowCosts(that, id, slider.getValue());
        } : function () {
            PhoenicianSalesman.unitShowCosts(that, id, slider.getValue());
        });
        if (res) {
            text = GameData.resources[inp[0].value];
            this.find('.ph_unit_order_unit_iron').text(property.cost.iron);
        } else {
            text = GameData.units[property.name].name;
        }
        this.find('.ph_unit_order_unit_name').text(text);
        try {
            slider.setMax(property.amount);
            slider.setValue(property.amount);
        } catch (e) {}
        if (property.amount <= 0) {
            this.fadeTo("slow", 0.2);
        }
    },
    unitShowCosts: function (elm, id, count) {
        var unit = PhoenicianSalesman.data.goods.units[id];
        var value = unit.cost.iron * count;
        elm.find('span.ph_unit_order_all_iron').text(value);
        elm.find('a.confirm').toggleClass('disabled', value < 1);
    },
    resourceShowCosts: function (elm, id, count) {
        var resource = PhoenicianSalesman.data.goods.resources[id];
        var value = Math.ceil(count / resource.cost[PhoenicianSalesman.data.goods.exchange_resource]);
        var i;
        for (i in resource.cost) {
            if (resource.cost[i]) {
                break;
            }
        }
        elm.find('span.ph_unit_order_all').text(value).prev()[0].className = 'unit_order_res ' + i + '_img';
        elm.find('a.confirm').toggleClass('disabled', value < 1);
    },
    showHint: function (headline) {
        if (!Layout.player_hint_settings.phoenician_salesman_hint || PhoenicianSalesman.hint_showed || (Tutorial && Tutorial.isActive())) {
            return;
        }
        PhoenicianSalesman.hint_showed = true;
        Layout.ajaxGet('phoenician_salesman', 'get_hint', {}, true, function (data) {
            (GPWindowMgr.Create(GPWindowMgr.TYPE_SHOW_ON_LOGIN) || GPWindowMgr.getOpenFirst(GPWindowMgr.TYPE_SHOW_ON_LOGIN)).getHandler().onRcvData(data.json);
        });
    }
};

var ITowns = new TownsData();

function TownsData() {
    var that = this;
    var towns = {};
    var townList = new TownList();
    var townGroups = new TownGroups();
    this.getNextTownId = function (town_id) {
        var town = townList.getListItem(town_id);
        if (!town) {
            return townGroups.getFirstTownIdInActiveGroup();
        }
        return town.next;
    };
    this.getPrevTownId = function (town_id) {
        var town = townList.getListItem(town_id);
        if (!town) {
            return townGroups.getFirstTownIdInActiveGroup();
        }
        return town.prev;
    };
    this.getTown = function (town_id) {
        return towns[town_id];
    };
    this.getTowns = function () {
        return towns;
    };
    this.numTowns = function () {
        return countProperties(towns);
    };
    this.getGod = function (town_id) {
        return towns[town_id].god();
    };
    this.getTownGroups = function () {
        return townGroups.getGroups();
    };
    this.getTownGroupsStopSortCallback = function () {
        return townGroups.stopSort;
    };
    this.getActiveTownGroup = function () {
        return townGroups.getActiveTownGroup();
    };
    this.getResources = function (town_id) {
        return towns[town_id].resources();
    };
    this.getSortedTownGroupMapByName = function (group_id) {
        var map = [];
        var group = townGroups.getGroups()[group_id];
        var tid;
        for (tid in group.towns) {
            if (!group.towns.hasOwnProperty(tid)) {
                continue;
            }
            map.push(tid);
        }
        map.sort(function (a, b) {
            var A = towns[a].name.toLowerCase();
            var B = towns[b].name.toLowerCase();
            if (A < B) {
                return -1;
            }
            if (A > B) {
                return 1;
            }
            return 0;
        });
        return map;
    };
    this.setTownGroupsDropdown = function (dropdown) {
        townGroups.setDropdown(dropdown);
    };
    this.setResources = function (res, town_id) {
        towns[town_id].resources(res);
    };
    this.setStorage = function (storage_volume, town_id) {
        towns[town_id].storage(storage_volume);
    };
    this.setResourcesConstraints = function (resc, town_id) {
        towns[town_id].resourcesConstraints(resc);
    };
    this.setProduction = function (prod, town_id) {
        towns[town_id].production(prod);
    };
    this.setGod = function (god, town_id) {
        towns[town_id].god(god);
    };
    this.setAllGodsFavor = function (town_id) {
        towns[town_id].god(god);
    };
    this.setName = function (name, town_id) {
        towns[town_id].name = name;
    };
    this.setActiveTownGroup = function (group_id) {
        townGroups.setActiveTownGroup(group_id);
    };
    this.updateResourceCounter = function (town_id) {
        var town = towns[town_id],
            prod = town.production(),
            res = town.resources(),
            resRem = town.resourcesRemainders(),
            new_res = {},
            res_produced = 0,
            elapsed_time = Timestamp.now() - town.getResourcesLastUpdate(),
            need_update = false,
            i;
        for (i in resRem) {
            res_produced = (prod[i] * elapsed_time) / 3600;
            if (res_produced >= 1) {
                new_res[i] = res[i] + ~~res_produced;
                resRem[i] += res_produced - ~~res_produced;
                if (resRem[i] >= 1) {
                    new_res[i] += 1;
                    resRem[i] -= 1;
                }
                need_update = true;
            } else if (need_update) {
                new_res[i] = res[i];
                resRem[i] += res_produced;
                if (resRem[i] >= 1) {
                    new_res[i] += 1;
                    resRem[i] -= 1;
                }
            }
        }
        if (need_update) {
            town.resources(new_res);
        }
    };
    this.townGroupsTownSwitch = function (group_id, town_id) {
        townGroups.townSwitch(group_id, town_id);
    };
    this.townGroupsRemoveFromGroup = function (group_id, town_id) {
        townGroups.removeFrom({
            town_id: town_id,
            group_id: group_id
        });
    };
    this.townGroupsAddToGroup = function (group_id, town_id) {
        townGroups.addTo({
            town_id: town_id,
            group_id: group_id
        });
    };
    this.resetResourceOffsets = function () {
        var idx;
        for (idx in that.towns) {
            towns[idx].resources.wood_offset = 0;
            towns[idx].resources.stone_offset = 0;
            towns[idx].resources.iron_offset = 0;
        }
    };
    this.initialize = function (data) {
        that.addToTowns(data.towns);
        townGroups.initialize(data);
        townList.rebuild(townGroups.getActiveTownGroup());
        $(document).bind('ITowns.refetch', ITowns.refetch);
    };
    this.refetch = function (additional_callback) {
        Layout.ajaxGet('town_group_overviews', 'get_all_towns', {}, true, function (data) {
            ITowns.addToTowns(data.towns);
            townGroups.initialize(data);
            townList.rebuild(townGroups.getActiveTownGroup());
            if (additional_callback) {
                additional_callback();
            }
        });
    };
    this.rebuildTownList = function () {
        townList.rebuild(townGroups.getActiveTownGroup());
    };
    this.addToTowns = function (towns_data) {
        var town, itown, i = towns_data.length;
        while (i--) {
            town = towns_data[i];
            if (!(itown = towns[town.id])) {
                itown = towns[town.id] = new Town(town.id, town.name);
            }
            itown.setIslandCoordinates(town.island_x, town.island_y);
            itown.addTownGroupId(town.group_id);
            itown.researches(town.researches);
            itown.resourcesConstraints({
                rare: town.rare,
                plenty: town.plenty
            });
        }
    };
}

function Town(id, name) {
    var fragment = '',
        capacity = 0,
        resources = {},
        researches = {},
        production = {},
        unitsInTown = {},
        powersInTown = {},
        unitsSupportInTown = {},
        unitsOuterTown = {},
        resources_contraints = {},
        resources_remainders = {
            'wood': 0,
            'stone': 0,
            'iron': 0,
            'favor': 0
        },
        resources_last_updated = 0,
        building_orders_completed = {},
        god = '',
        points = 0,
        all_gods_favors = {},
        that = this;
    this.id = parseInt(id, 10);
    this.name = name;
    this.island_x = null;
    this.island_y = null;
    this.group_ids = {};

    function modUnits(_units, ref_units, type) {
        var i;
        for (i in _units) {
            if (GameData.units[i]) {
                switch (type) {
                case 'sub':
                    ref_units[i] -= _units[i];
                    break;
                case 'add':
                    ref_units[i] += _units[i];
                    break;
                default:
                    ref_units[i] = _units[i];
                }
            }
        }
        if (that.id == Game.townId && ref_units == unitsInTown) {
            GPEvents.global.trigger(GPEvents.global.town.units_changed);
        }
        return ref_units;
    }

    function modPowers(_powers, ref_powers, type) {
        var i = _powers.length;
        while (i--) {
            if (GameData.powers[_powers[i]]) {
                switch (type) {
                case 'sub':
                    ref_powers[_powers[i]] -= _powers[i];
                    break;
                case 'add':
                    ref_powers[_powers[i]] += _powers[i];
                    break;
                default:
                    ref_powers[_powers[i]] = _powers[i];
                }
            }
        }
        if (that.id == Game.townId && ref_powers == powersInTown) {
            GPEvents.global.trigger(GPEvents.global.town.resources_update);
        }
        return ref_powers;
    }
    this.fragment = function (_fragment) {
        if (_fragment) {
            fragment = _fragment;
            return this;
        }
        return fragment;
    };
    this.researches = function (_researches) {
        var i;
        if (_researches) {
            for (i in _researches) {
                researches[i] = _researches[i];
            }
            return this;
        }
        return researches;
    };
    this.addTownGroupId = function (group_id) {
        that.group_ids[group_id] = group_id;
    };
    this.removeTownGroupId = function (group_id) {
        if (that.group_ids[group_id]) {
            delete that.group_ids[group_id];
        }
    };
    this.getTownGroupIds = function () {
        return that.group_ids;
    };
    this.setIslandCoordinates = function (_x, _y) {
        that.island_x = _x;
        that.island_y = _y;
    };
    this.getIslandCoordinates = function () {
        return {
            island_x: that.island_x,
            island_y: that.island_y
        };
    };
    this.getResourcesLastUpdate = function () {
        return resources_last_updated;
    };
    this.resourcesRemainders = function (_resrem) {
        var i;
        if (!_resrem) {
            return resources_remainders;
        }
        for (i in _resrem) {
            if (i.match(/wood|stone|iron|favor/)) {
                resources_remainders[i] = _resrem[i];
            }
        }
    };
    this.resources = function (_res) {
        var storage, i, oldres = {},
            res;
        if (!_res) {
            return resources;
        }
        for (i in _res) {
            if (i == 'favor') {
                storage = 500;
            } else {
                if (!isNumber(resources.storage)) {
                    storage = parseInt(_res.storage, 10);
                } else {
                    storage = resources.storage;
                }
            }
            if (i.match(/wood|stone|iron|favor/) && isNumber(storage) && _res[i] > storage) {
                _res[i] = storage;
            }
            res = parseInt(_res[i], 10);
            oldres[i] = res - resources[i];
            resources[i] = res;
        }
        resources_last_updated = Timestamp.now();
        if (that.id == Game.townId) {
            var g = GPEvents.global;
            g.trigger(g.town.resources_update, oldres);
        }
        return this;
    };
    this.resourcesConstraints = function (_resc) {
        var i, c;
        if (!_resc) {
            return resources_contraints;
        }
        for (i in _resc) {
            c = _resc[i];
            if (i.match(/rare|plenty/) && c.match(/wood|stone|iron/)) {
                resources_contraints[i] = c;
            }
        }
        return this;
    };
    this.points = function (_points) {
        if (_points === undefined) {
            return points;
        }
        points = _points;
        return this;
    };
    this.render = function (tmpl, additionalData) {
        var html;
        this.addD = additionalData || {};
        html = tmpl2(tmpl, this);
        delete this.addD;
        return html;
    };
    this.production = function (_prod) {
        var i;
        if (!_prod) {
            return production;
        }
        for (i in _prod) {
            production[i] = parseInt(_prod[i], 10);
        }
        return this;
    };
    this.capacity = function (_cap) {
        if (typeof _cap !== typeof 0) {
            return capacity;
        }
        capacity = _cap;
        return this;
    };
    this.getCurrentResources = function () {
        var now = Timestamp.now(),
            current = {},
            i, storage = this.storage();
        for (i in production) {
            current[i] = Math.floor(Math.min(storage, resources[i] + ((now - resources_last_updated) / 36E2 * production[i])));
        }
        return current;
    };
    this.population = function (_pop) {
        if (_pop === undefined) {
            return resources.population;
        }
        resources.population = _pop;
        return this;
    };
    this.buildingOrdersETC = function (_orders_etcs) {
        var i;
        if (!_orders_etcs) {
            return building_orders_completed;
        }
        building_orders_completed = {};
        for (i in _orders_etcs) {
            if (_orders_etcs.hasOwnProperty(i)) {
                building_orders_completed[i] = _orders_etcs[i];
            }
        }
        return this;
    };
    this.god = function (_god) {
        if (!_god) {
            return god;
        }
        god = _god;
        return this;
    };
    this.allGodsFavors = function (_all_gods_favors) {
        if (!_all_gods_favors) {
            return all_gods_favors;
        }
        all_gods_favors = _all_gods_favors;
        return this;
    };
    this.storage = function (_storage) {
        if (!isNumber(_storage)) {
            return resources.storage;
        }
        resources.storage = parseInt(_storage, 10);
        return this;
    };
    this.isMax = function (_res) {
        return _res.match(/pop/) ? resources[_res] <= 0 : resources.storage == resources[_res];
    };
    this.units = function (_units) {
        if (!_units) {
            return unitsInTown;
        }
        return modUnits(_units, unitsInTown, 'replace');
    };
    this.subtractUnits = function (_units) {
        return modUnits(_units, unitsInTown, 'sub');
    };
    this.addUnits = function (_units) {
        return modUnits(_units, unitsInTown, 'add');
    };
    this.unitsOuter = function (_units) {
        if (!_units) {
            return unitsOuterTown;
        }
        return modUnits(_units, unitsOuterTown, 'replace');
    };
    this.subtractUnitsOuter = function (_units) {
        return modUnits(_units, unitsOuterTown, 'sub');
    };
    this.addUnitsOuter = function (_units) {
        return modUnits(_units, unitsOuterTown, 'add');
    };
    this.powers = function (_powers) {
        if (!_powers) {
            return powersInTown;
        }
        return modPowers(_powers, powersInTown, 'replace');
    };
    this.unitsSupport = function (_units) {
        if (!_units) {
            return unitsSupportInTown;
        }
        return modUnits(_units, unitsSupportInTown, 'replace');
    };
    this.subtractUnitsSupport = function (_units) {
        return modUnits(_units, unitsSupportInTown, 'sub');
    };
    this.addUnitsSupport = function (_units) {
        return modUnits(_units, unitsSupportInTown, 'add');
    };
}

function TownGroups() {
    var that = this,
        active_group_id = '0',
        groups = {},
        dropdown = null;
    this.initialize = function (data) {
        var i, group, virtual_group = {
            id: '0',
            name: 'virtual',
            towns: {}
        };
        groups = {};
        active_group_id = '0';
        if (!data.groups) {
            groups = {
                'null': {
                    id: null,
                    name: null,
                    towns: {}
                }
            };
        } else {
            for (i = 0; i < data.groups.length; i++) {
                group = data.groups[i];
                groups[group.id] = {
                    id: group.id,
                    name: group.name,
                    towns: {}
                };
                if (group.active) {
                    active_group_id = group.id;
                }
            }
        }
        for (i = 0; i < data.towns.length; i++) {
            var gid = data.towns[i].group_id || null,
                tid = data.towns[i].id;
            if (!groups[gid]) {
                groups[gid] = {
                    id: gid,
                    towns: {}
                };
            }
            groups[gid].towns[tid] = {
                id: data.towns[i].id,
                group_id: gid
            };
            virtual_group.towns[tid] = {
                id: data.towns[i].id
            };
        }
        if (data.tmpl) {
            GameData.add({
                'TownListTemplate': data.tmpl
            });
        }
        groups['0'] = virtual_group;
        Layout.townNameList.createTownListDropdown(true);
    };
    this.townSwitch = function (gid, tid) {
        Layout.townSwitch(tid);
        that.setActiveTownGroup(gid);
    };
    this.setDropdown = function (_dropdown) {
        dropdown = _dropdown;
    };
    this.addTo = function (params) {
        Layout.ajaxPost('town_group_overviews', 'add_town_to_group', params, false, that.callbackAdd);
    };
    this.callbackAdd = function (return_data) {
        if (return_data.success) {
            var new_gid = return_data.group_id;
            var tid = return_data.town_id;
            var old_gid = that.findGroupIdByTownId(tid);
            if (old_gid === null) {
                groups[new_gid].towns[tid] = {
                    id: groups['null'].towns[tid].id,
                    group_id: groups['null'].towns[tid].group_id
                };
                delete groups['null'].towns[tid];
                ITowns.getTown(tid).removeTownGroupId('null');
            } else if (old_gid) {
                groups[new_gid].towns[tid] = {
                    id: tid,
                    group_id: new_gid
                };
            }
            groups[new_gid].towns[tid].group_id = new_gid;
            ITowns.getTown(tid).addTownGroupId(new_gid);
            ITowns.rebuildTownList();
            Layout.townNameList.createTownListDropdown(true);
            $(document).trigger('ITowns.TownGroups.added', return_data);
        }
    };
    this.removeFrom = function (params) {
        Layout.ajaxPost('town_group_overviews', 'remove_town_from_group', params, false, that.callbackRem);
    };
    this.callbackRem = function (return_data) {
        if (return_data.success) {
            var gid = return_data.group_id;
            var tid = return_data.town_id;
            that.remove(tid, gid);
            if (that.isGroupEmpty(gid) && gid === active_group_id) {
                that.setActiveTownGroup('0');
            }
            ITowns.getTown(tid).removeTownGroupId(gid);
            ITowns.rebuildTownList();
            Layout.townNameList.createTownListDropdown(true);
            $(document).trigger('ITowns.TownGroups.removed', return_data);
        }
    };
    this.remove = function (tid, gid) {
        var is_in_no_other_group = true,
            virtual_group_id = '0',
            i;
        for (i in groups) {
            if (i !== 'null' && i !== virtual_group_id && i !== gid.toString() && groups[i].towns[tid]) {
                is_in_no_other_group = false;
            }
        }
        if (is_in_no_other_group) {
            if (!groups['null']) {
                groups['null'] = {
                    id: null,
                    name: null,
                    towns: {}
                };
            }
            groups['null'].towns[tid] = {
                id: groups[gid].towns[tid].id,
                group_id: null
            };
            ITowns.getTown(tid).addTownGroupId('null');
        }
        if (groups[gid]) {
            delete groups[gid].towns[tid];
        }
    };
    this.setActiveTownGroup = function (id) {
        if (id == 'null') {
            id = '0';
        }
        if (that.isGroupEmpty(id) || id == active_group_id) {
            return;
        }
        Layout.ajaxPost('town_group_overviews', 'set_active_town_group', {
            'group_id': id
        }, false, function (data) {
            if (data.success) {
                active_group_id = data.town_group_id;
                if (dropdown) {
                    dropdown.find('fieldset').removeClass('active');
                    dropdown.find('#g' + data.town_group_id).parent().addClass('active');
                }
                ITowns.rebuildTownList();
                $(document).trigger('ITowns.TownGroups.setActiveTownGroup', data);
            }
        });
        Layout.townNameList.createTownListDropdown(true);
    };
    this.getFirstTownIdInGroup = function (group) {
        var first;
        for (first in group.towns) {
            if (group.towns.hasOwnProperty(first)) {
                return group.towns[first].id;
            }
        }
        return null;
    };
    this.getFirstTownIdInActiveGroup = function () {
        var active_group = that.getActiveTownGroup();
        return that.getFirstTownIdInGroup(active_group);
    };
    this.getActiveTownGroup = function () {
        return groups[active_group_id];
    };
    this.getGroups = function () {
        return groups;
    };
    this.isGroupEmpty = function (id) {
        return that.getFirstTownIdInGroup(groups[id]) === null;
    };
    this.findGroupIdByTownId = function (town_id) {
        var i;
        for (i in groups) {
            if (groups[i].id !== '0' && groups[i].towns[town_id]) {
                return groups[i].id;
            }
        }
        return -1;
    };
    this.stopSort = function (ev, ui) {
        var params = {
            town_id: ui.item[0].id.match(/\d+/)[0],
            group_id: ui.item[0].parentNode.id.substr(1)
        };
        var old_gid = ui.item[0].id.match(/\d+/g)[1];
        if (groups[params.group_id].towns[params.town_id]) {
            return false;
        }
        if (params.group_id === 'null') {
            params.group_id = old_gid;
            that.removeFrom(params);
        } else {
            that.addTo(params);
        }
    };
}

function TownList() {
    var that = this,
        towns_id_list = [],
        towns_id_linked_list = {};

    function buildTownIdArray(towns) {
        var i;
        for (i in towns) {
            towns_id_list.push(towns[i].id);
        }
    }

    function buildLinkedTownListMap() {
        var town_id, ntown_id, ptown_id, i;
        for (i = 0; i < towns_id_list.length; i++) {
            town_id = towns_id_list[i];
            ntown_id = towns_id_list[(i + 1) % towns_id_list.length];
            ptown_id = towns_id_list[((i - 1 < 0) ? (towns_id_list.length - 1) : (i - 1))];
            towns_id_linked_list[town_id] = {
                'next': ntown_id,
                'prev': ptown_id
            };
        }
    }
    this.rebuild = function (group_data) {
        towns_id_list = [];
        towns_id_linked_list = {};
        towns_id_list = ITowns.getSortedTownGroupMapByName(group_data.id);
        buildLinkedTownListMap();
    };
    this.getListItem = function (town_id) {
        return towns_id_linked_list[town_id];
    };
}

FarmTownBars = new function () {
    var that = this;
    var farm_towns = [];
    var farm_towns_lock = false;
    var timer_interval = 2E5;
    var timer_handle = null;
    var time_base = {};
    var enabled = false,
        prefix;
    this.initialize = function () {
        enabled = true;
        var b = $.browser;
        if (b.mozilla && b.version >= '1.9.1') {
            prefix = '-moz';
        } else if (b.webkit && b.version >= '525') {
            prefix = '-webkit';
        } else if (b.opera && parseFloat(b.version) >= parseFloat('10.5')) {
            prefix = '-o';
        } else if (b.msie && b.version >= '9') {
            prefix = '-ms';
        } else if (b.msie && b.version >= '7') {
            prefix = false;
        }
        timer_handle = window.setInterval(update, timer_interval);
    };
    this.isEnabled = function () {
        return enabled;
    };
    this.setVisibleTowns = function (towns) {
        if (!enabled) return;
        if (farm_towns_lock) {
            debug('FarmTownBars.farm_towns_lock = true');
            return;
        }
        farm_towns_lock = true;
        farm_towns = [];
        var i = towns.length,
            town_type;
        while (i--) {
            town_type = WMap.getTownType(towns[i]);
            if (town_type == 'farm_town' && towns[i].relation_status == 1) {
                farm_towns.push({
                    'id': towns[i].id,
                    'loot': towns[i].loot,
                    'looted': towns[i].looted,
                    'town_obj_ref': towns[i]
                });
            }
        }
        farm_towns_lock = false;
        update();
    };

    function f(x) {
        if (x < 0.6) return 0.208333 * x;
        else if (x < 0.9333) return 0.375038 * x - 0.100023;
        else if (x < 0.9833) return 5 * x - 4.4165;
        else return 29.9401 * x - 28.9401;
    }

    function update() {
        if (!enabled) return;
        if (farm_towns_lock) {
            debug('FarmTownBars.farm_towns_lock = true');
            return;
        }
        farm_towns_lock = true;
        if (farm_towns.length == 0) {
            farm_towns_lock = false;
            return;
        }
        var copy_farm_towns = [],
            i = farm_towns.length;
        while (i--) {
            copy_farm_towns.push({
                'id': farm_towns[i].id,
                'loot': farm_towns[i].loot,
                'looted': farm_towns[i].looted,
                'town_obj_ref': farm_towns[i].town_obj_ref
            });
        }
        farm_towns_lock = false;
        var town, id, town_data, interval, values;
        i = copy_farm_towns.length;
        while (i--) {
            town_data = copy_farm_towns[i];
            id = town_data.id;
            if (town_data.loot <= Timestamp.now()) {
                delete time_base[id];
                continue;
            }
            town = $('#farm_town_' + id);
            if (!town.hasClass('res_bar')) {
                town.append('<span class="res_bar"><span class="res_bar_inner"></span><span class="res_available not"></span></span>');
                town_data.town_obj_ref.popup = null;
            }
            if (!time_base[id]) {
                interval = town_data.loot - town_data.looted;
                values = GameData.farm_town_time_values;
                if (values.normal.indexOf(interval) >= 0) time_base[id] = values.normal[values.normal.length - 1];
                else if (values.booty.indexOf(interval) >= 0) time_base[id] = values.booty[values.booty.length - 1];
            }
            var x = (1 - (town_data.loot - Timestamp.now()) / time_base[id]);
            var rad = f(x) * Math.PI;
            var grad = (Math.round((rad * 180 / Math.PI)) % 360);
            if (prefix) {
                town.find('span.res_bar_inner').attr('style', prefix + '-transform:rotate(' + grad + 'deg)');
            } else {
                var cos = Math.cos(rad);
                var sin = Math.sin(rad);
                var bar = town.find('span.res_bar_inner');
                bar[0].style.filter = 'progid:DXImageTransform.Microsoft.Matrix(FilterType="bilinear", sizingMethod="auto expand",M11 = ' + cos + ', M12 = ' + (-sin) + ', M21 = ' + sin + ', M22 = ' + cos + ');';
                var w = -bar.width() / 2 + 15;
                var h = -bar.height() / 2 + 17;
                bar[0].style.left = w + 'px';
                bar[0].style.top = h + 'px';
            }
        }
    }
}

var GPEvents = {
    global: {
        spend_gold: 'spend_gold',
        overviews: {
            power_casted: 'power_casted',
            favor_changed: 'favor_changed'
        },
        unit_orders: {
            unit_finished: 'unit_finished'
        },
        system_notifications: {
            conquest_info: 'notification.conquestInfo',
            uninhabited_place_colonized: 'notification.uninhabitedPlaceColonized'
        },
        map: {
            mapjump: 'mapjump'
        },
        forum: {
            contents_set: 'forum_contents_set'
        },
        town: {
            units_changed: 'units_changed',
            outer_units_changed: 'outer_units_changed',
            town_switch: 'townSwitch',
            resources_update: 'resources_update',
            commands_updated: 'commands_updated',
            building_order_changed: 'building_order_changed',
            unit_order_changed: 'unit_order_changed',
            hide_has_changed: 'hide_has_changed'
        },
        command: {
            send_back_support: 'send_back_support'
        },
        trigger: function (event, data) {
            $(document).trigger(event, data);
        },
        bind: function (event, callback) {
            $(document).bind(event, callback);
        },
        unbind: function (event, callback) {
            if (callback) {
                $(document).unbind(event, callback);
            } else {
                $(document).unbind(event);
            }
        }
    },
    local: {}
};

var BuildOrders = {
    orders: null,
    finish_for_gold_dialog_text: null,
    onConfirmFinishForGold: "BuildOrders.finishForGold(w(this))",
    building_cancel_dialog_text: null,
    onConfirmBuildingCancel: "BuildOrders.cancelBuilding(w(this))",
    txtFinishPopup: null,
    order_id_to_finish: null,
    fired_building_events: {},
    building_finish_event_active: false,
    availableGold: 0,
    finishGoldOrderCost: 0,
    indexImageCountdown: null,
    mainImageCountdown: null,
    notEnoughGoldWindow: null,
    remaining_reductions_until_free: 5,
    is_finish_order_campaign_active: false,
    init: function (orders, availableGold, finishGoldOrderCost, remaining_reductions_until_free, is_finish_order_campaign_active) {
        BuildOrders.orders = orders;
        BuildOrders.availableGold = availableGold;
        BuildOrders.finishGoldOrderCost = finishGoldOrderCost;
        BuildOrders.remaining_reductions_until_free = remaining_reductions_until_free;
        BuildOrders.is_finish_order_campaign_active = is_finish_order_campaign_active;
        BuildOrders.finish_for_gold_dialog_text = s(ngettext('Are you sure you want to cut the construction time in half for %1 gold?', new Array('Are you sure you want to cut the construction time in half for %1 gold?', 'Are you sure you want to cut the construction time in half for %1 gold?'), finishGoldOrderCost), finishGoldOrderCost);
        BuildOrders.building_cancel_dialog_text = 'Are you sure you want to cancel the building construction?';
        BuildOrders.txtFinishPopup = ['<span class="bold">', s(ngettext('You can cut the construction time in half for %1 gold.', new Array('You can cut the construction time in half for %1 gold.', 'You can cut the construction time in half for %1 gold.'), finishGoldOrderCost), finishGoldOrderCost), '<br /><br />', s(ngettext('Available gold: %1', new Array('Available gold: %1', 'Available gold: %1'), availableGold), availableGold)].join('');
        if (is_finish_order_campaign_active) {
            BuildOrders.txtFinishPopup += '<br /><br />';
            if (remaining_reductions_until_free == 0) {
                BuildOrders.txtFinishPopup += 'This 50% construction time reduction is free';
            } else {
                BuildOrders.txtFinishPopup += s(ngettext('Only %1 more 50% construction time reduction, then you will receive a free one', new Array('Only %1 more 50% construction time reduction, then you will receive a free one', 'Only %1 more 50% construction time reductions, then you will receive a free one'), remaining_reductions_until_free), remaining_reductions_until_free);
            }
        }
        BuildOrders.txtFinishPopup += '</span>';
    },
    setLength: function (orders) {
        orders.length = 0;
        var i;
        for (i in orders) {
            if (i != 'length') {
                orders.length++;
            }
        }
    },
    initPopups: function () {
        $('a.main_tasks_finish').mousePopup(new MousePopup(BuildOrders.txtFinishPopup));
        var i;
        for (i in BuildOrders.orders) {
            var order = BuildOrders.orders[i];
            if (i >= 0 && i < 8) {
                if (i == 0) {
                    $('#building_tasks_main div.main_tasks_0 div.image_countdown').mousePopup(new MousePopup(order.building_name));
                    $('#building_tasks_index div.main_tasks_0 div.image_countdown').mousePopup(new MousePopup(order.building_name));
                } else {
                    $('#building_tasks_main div.main_tasks_' + i + ' div.main_tasks_image').mousePopup(new MousePopup(order.building_name));
                    $('#building_tasks_index div.main_tasks_' + i + ' div.main_tasks_image').mousePopup(new MousePopup(order.building_name));
                }
                if (order.cancel_refund) {
                    var txtRefund = 'Cancel building construction' + '<br />' + s('Reimbursement: %1', formatResources(order.cancel_refund));
                    $('a.main_tasks_cancel').mousePopup(new MousePopup(txtRefund));
                }
                $('div.main_tasks_' + i + ' div.main_task_time').mousePopup(new MousePopup(s('Completion %1', order.completed_human)));
            }
        }
    },
    startCountdown: function () {
        if (this.orders.length > 0) {
            var building_order_current = $('div.main_tasks_0 div.main_task_time');
            building_order_current.countdown(this.orders[0].to_be_completed_at, {});
            building_order_current.bind('finish', function () {
                BuildingMain.buildingFinished();
            });
            BuildOrders.startImageCountdown();
        }
    },
    startImageCountdown: function () {
        if (this.orders[0] != undefined) {
            var building_order_main, building_order_index;
            var completed_at = this.orders[0].to_be_completed_at;
            var start = completed_at - this.orders[0].building_time;
            var img_countdown_running = 0;
            if (BuildingMain.isMainWindowOpen()) {
                building_order_main = $('#building_tasks_main div.main_tasks_0 div.main_tasks_image');
                img_countdown_running = $('#building_tasks_main div.main_tasks_0 div.image_countdown').length;
                if (img_countdown_running == 0) {
                    this.mainImageCountdown = new ImageCountdown(building_order_main, start, completed_at, {}, {
                        'width': '40px',
                        'height': '2560px'
                    });
                }
            }
            if (BuildingMain.isIndexWindowOpen()) {
                building_order_index = $('#building_tasks_index div.main_tasks_0 div.main_tasks_image');
                img_countdown_running = $('#building_tasks_index div.main_tasks_0 div.image_countdown').length;
                if (img_countdown_running == 0) {
                    this.indexImageCountdown = new ImageCountdown(building_order_index, start, completed_at, {}, {
                        'width': '40px',
                        'height': '2560px'
                    });
                }
            }
        }
    },
    confirm_finish_for_gold: function (order_id, wnd, button) {
        var disabled = 'disabled',
            confirm, cancel, event_postfix = ABTests.getKontagentAbcEventPostfix(GameData.abctest_time_reduction);
        button = $(button);
        if (button.hasClass(disabled)) {
            return;
        }
        Kontagent.trackEvent({
            userId: Game.uid,
            eventName: 'click_enough_gold' + event_postfix,
            subtype1: 'FunnelData',
            subtype2: 'time_reduction_btn_2'
        }, function () {});
        if (wnd) {
            this.wnd = wnd;
        }

        function callback() {
            var params = {
                'order_id': order_id
            },
                action = 'finish_for_gold';
            Layout.ajaxPost('building_main', action, params, true, function (data) {
                var ev = GPEvents.global;
                if (data.success) {
                    var event_postfix = ABTests.getKontagentAbcEventPostfix(GameData.abctest_time_reduction);
                    if (event_postfix != "") {
                        Kontagent.trackEvent({
                            userId: Game.uid,
                            eventName: 'finish_for_gold' + event_postfix,
                            subtype1: 'FunnelData',
                            subtype2: 'time_reduction_btn_2'
                        }, function () {});
                    }
                    ev.trigger(ev.spend_gold);
                }
            });
        }
        button.addClass(disabled);
        if (Layout.show_confirmation_popup) {
            confirm = function () {
                callback();
            };
            cancel = function () {
                button.removeClass(disabled);
            };
            Layout.showConfirmDialog('Cut the construction time in half', BuildOrders.finish_for_gold_dialog_text, confirm, null, cancel, null, null, 'Do not show this window again');
        } else {
            callback();
        }
    },
    showNotEnoughGoldWindow: function (completed_at, completed_at_prev) {
        var event_postfix = ABTests.getKontagentAbcEventPostfix(GameData.abctest_time_reduction);
        if (event_postfix != "") {
            Kontagent.trackEvent({
                userId: Game.uid,
                eventName: 'click_not_enough_gold' + event_postfix,
                subtype1: 'FunnelData',
                subtype2: 'time_reduction_btn_2'
            }, function () {});
        }
        Kontagent.trackEvent({
            userId: Game.uid,
            eventName: 'click_not_enough_gold',
            subtype1: 'FunnelData',
            subtype2: 'time_reduction_btn_2'
        }, function () {});
        Layout.ajaxGet('building_main', 'tr_not_enough_gold_buildings', {}, false, function (data) {
            var html = tmpl(data.html, {
                'completed_at': completed_at,
                'completed_at_prev': completed_at_prev
            });
            if (!BuildOrders.notEnoughGoldWindow) {
                BuildOrders.notEnoughGoldWindow = Layout.dialogWindow.open(html, 'Cut the construction time in half', 496, 360, function () {
                    BuildOrders.notEnoughGoldWindow = null;
                }, true);
            }
        });
    },
    showBuyGoldWindow: function () {
        var event_postfix = ABTests.getKontagentAbcEventPostfix(GameData.abctest_time_reduction);
        if (event_postfix != "") {
            Kontagent.trackEvent({
                userId: Game.uid,
                eventName: 'click_show_buy_gold_wnd' + event_postfix,
                subtype1: 'FunnelData',
                subtype2: 'time_reduction_btn_2'
            }, function () {});
        }
        Kontagent.trackEvent({
            userId: Game.uid,
            eventName: 'click_show_buy_gold_wnd',
            subtype1: 'FunnelData',
            subtype2: 'time_reduction_btn_2'
        }, function () {});
        GPWindowMgr.Create(GPWindowMgr.TYPE_PREMIUM, 'Premium', {
            sub_content: 'buy_gold'
        });
    },
    confirm_cancel_building: function (wnd) {
        this.wnd = wnd;
        Layout.showConfirmDialog('Cancel building construction', BuildOrders.building_cancel_dialog_text, function () {
            BuildingMain.cancelBuilding();
        });
    },
    finishForGold: function (wnd) {
        wnd.close();
        BuildingMain.finishForGold(BuildOrders.order_id_to_finish);
    },
    cancelBuilding: function (wnd) {
        wnd.close();
        BuildingMain.cancelBuilding();
    },
    goldChanged: function (new_gold) {
        if (BuildingMain.isMainWindowOpen() || BuildingMain.isIndexWindowOpen()) {
            BuildOrders.availableGold = new_gold;
            BuildOrders.init(BuildOrders.orders, new_gold, BuildOrders.finishGoldOrderCost, BuildOrders.remaining_reductions_until_free, BuildOrders.is_finish_order_campaign_active);
            BuildOrders.updateBuildingOrders();
        }
    },
    updateBuildingOrders: function (orders) {
        if (orders) {
            BuildOrders.orders = orders;
        }
        var html = tmpl2(GameData.BuildingOrdersTemplate, {
            'building_orders': BuildOrders.orders
        });
        var html2 = tmpl2(GameData.BuildingOrdersTemplateNew, {
            'building_orders': BuildOrders.orders
        });
        var abctest = GameData.abctest_time_reduction;
        $('#building_tasks_main').html(abctest == 'B' || abctest == 'C' ? html2 : html);
        if (BuildingMain.isBuildFromTownIndexEnabled()) {
            $('#building_tasks_index').html(html);
        }
        BuildOrders.startCountdown();
        BuildOrders.initPopups();
    },
    handleEvents: function (event) {
        var orders = ITowns.getTown(Game.townId).buildingOrdersETC();
        if (orders[0] == undefined) {
            return;
        } else {
            if (BuildOrders.building_order_event_active || BuildOrders.fired_building_events[orders[0].to_be_completed_at] == orders[0].building_type) {
                return;
            }
            if (orders.length == undefined) {
                BuildOrders.setLength(orders);
            }
            BuildOrders.building_order_event_active = true;
            BuildOrders.updateBuildingOrders(orders);
            BuildOrders.fired_building_events[orders[0].to_be_completed_at] = orders.building_type;
            BuildOrders.building_order_event_active = false;
        }
    }
};

var MenuBubbleOrders = {
    units: null,
    orders: null,
    orders_count: 0,
    menu_bubble: null,
    timeout_handles: {},
    availableGold: 0,
    finishGoldOrderCost: 0,
    finish_for_gold_enabled: false,
    renderHtml: function (data) {
        if (data.json.troops) {
            if (data.tmpl.troops != null) {
                GameData.add({
                    'MenuBubbleOrdersTemplate': data.tmpl.troops
                });
            } else {
                data.tmpl.troops = GameData.MenuBubbleOrdersTemplate;
                if (GameData.MenuBubbleOrdersTemplate == null || GameData.MenuBubbleOrdersTemplate == undefined) {
                    return '';
                }
            }
            MenuBubbleOrders.orders = data.json.troops.orders;
            MenuBubbleOrders.units = data.json.troops.units;
            MenuBubbleOrders.finish_for_gold_enabled = data.json.troops.build_time_reduction_enabled;
            return tmpl2(data.tmpl.troops, data.json.troops);
        }
        return '';
    },
    redrawOrderCount: function () {
        if (MenuBubbleOrders.menu_bubble) {
            var new_height = MenuBubbleOrders.orders_count * 50;
            new_height = new_height > 370 ? 370 : new_height;
            MenuBubbleOrders.menu_bubble.changeHeight(new_height);
            MenuBubbleOrders.menu_bubble.getjIconElem().html(MenuBubbleOrders.orders_count);
        }
    },
    cancelOrder: function (unit_type) {
        var controller = '';
        if (unit_type == 'ground') {
            controller = 'building_barracks';
        } else if (unit_type == 'naval') {
            controller = 'building_docks';
        }
        if (controller != '') {
            Layout.ajax(controller, 'cancel', {}, true, function () {
                GPEvents.global.trigger(GPEvents.global.town.unit_order_changed);
            });
        }
    },
    finishForGold: function (order_id, unit_type, button) {
        var controller = '',
            finish_for_gold_dialog_text = '',
            disabled = 'disabled',
            confirm, cancel;
        button = $(button);
        if (button.hasClass(disabled)) {
            return;
        }
        if (unit_type == 'ground') {
            finish_for_gold_dialog_text = s(ngettext('Are you sure you want to cut the recruitment time in half for %1 gold?', new Array('Are you sure you want to cut the recruitment time in half for %1 gold?', 'Are you sure you want to cut the recruitment time in half for %1 gold?'), 20), 20);
            controller = 'building_barracks';
        } else if (unit_type == 'naval') {
            finish_for_gold_dialog_text = s(ngettext('Are you sure you want to cut the construction time in half for %1 gold?', new Array('Are you sure you want to cut the construction time in half for %1 gold?', 'Are you sure you want to cut the construction time in half for %1 gold?'), 20), 20);
            controller = 'building_docks';
        } else {
            return;
        }
        button.addClass(disabled);
        confirm = function () {
            Layout.ajaxPost(controller, 'finish_for_gold', {
                order_id: order_id
            }, true, function () {
                GPEvents.global.trigger(GPEvents.global.town.unit_order_changed);
            });
        };
        if (Layout.show_confirmation_popup) {
            cancel = function () {
                button.removeClass(disabled);
            };
            Layout.showConfirmDialog((unit_type == 'ground' ? 'Reduce the recruitment time by half' : 'Cut the construction time in half'), finish_for_gold_dialog_text, confirm, null, cancel, null, null, 'Do not show this window again');
        } else {
            confirm();
        }
    },
    finishCountdownCallback: function () {
        var ul_item = $(this).parent().parent();
        $(this).parent().remove();
        MenuBubbleOrders.orders_count--;
        if (MenuBubbleOrders.orders_count <= 0) {
            MenuBubbleOrders.orders_count = 0;
            ul_item.html('No construction orders');
        }
        MenuBubbleOrders.redrawOrderCount();
        var bits = Layout.getBarBitMask();
        Layout.refetchBar(null, {
            param_id: Game.townId,
            param_str: bits.LAYOUT_BAR_UNITS | bits.LAYOUT_BAR_MENU_BUBBLES | bits.LAYOUT_BAR_RESOURCES
        });
    },
    startCountdown: function () {
        var i;
        for (i in this.timeout_handles) {
            if (this.timeout_handles.hasOwnProperty(i)) {
                window.clearTimeout(this.timeout_handles[i]);
            }
        }
        if (MenuBubbleOrders.orders != null && MenuBubbleOrders.orders.length > 0) {
            $('#orders span.eta').each(function (i, span) {
                var data = MenuBubbleOrders.orders[i],
                    popup_text = "";
                span = $(span);
                MenuBubbleOrders.finishedHandlerUnit(false, data);
                popup_text += 'Completion:' + data.completed_human;
                span.countdown(data.to_be_completed_at, {}).bind('finish', MenuBubbleOrders.finishCountdownCallback).mousePopup(new MousePopup(popup_text));
                if (data.refund) {
                    var refund_popup_text = s('Reimbursement of wood: %1 rock: %2 silver coins: %3 favor: %4', data.refund.resources.wood, data.refund.resources.stone, data.refund.resources.iron, data.refund.favor);
                    data.jQElemPopup = span.parent().find('a.cancel');
                    data.jQElemPopup.mousePopup(new MousePopup(refund_popup_text));
                }
            });
        }
    },
    finishedHandlerUnit: function (self_called, order) {
        var end_time_unit;
        if (self_called) {
            if (order.units_left > 0) {
                order.units_left--;
                order.seconds_left -= MenuBubbleOrders.units[order.unit_id].build_time;
                $('#order_' + order.type + order.to_be_completed_at + ' div.menu_bubble_orders_left').html(order.units_left);
                UnitOrder.redrawCancelRefund(order, order.jQElemPopup);
                GPEvents.global.trigger(GPEvents.global.unit_orders.unit_finished, order);
                if (order.units_left <= 0) {
                    return;
                }
            } else if (order.unitTimeoutHandle != null) {
                window.clearTimeout(order.unitTimeoutHandle);
            }
        } else if (order.unitTimeoutHandle != null) {
            window.clearTimeout(order.unitTimeoutHandle);
        }
        var build_time = ~~ ((order.to_be_completed_at - order.created_at) / order.count);
        end_time_unit = order.to_be_completed_at - ((order.units_left - 1) * build_time) - Timestamp.now();
        order.unitTimeoutHandle = window.setTimeout(function () {
            MenuBubbleOrders.finishedHandlerUnit(true, order);
        }, end_time_unit * 1000);
        MenuBubbleOrders.timeout_handles[order.type + order.to_be_completed_at] = order.unitTimeoutHandle;
    }
};